@rtinternal/java
Advanced tools
+1404
| #include "java.h" | ||
| #include <string> | ||
| #ifdef WIN32 | ||
| #else | ||
| #include <unistd.h> | ||
| #endif | ||
| #include "javaObject.h" | ||
| #include "javaScope.h" | ||
| #include "methodCallBaton.h" | ||
| #include "node_NodeDynamicProxyClass.h" | ||
| #include <node_version.h> | ||
| #include <queue> | ||
| #include <sstream> | ||
| #include <nan.h> | ||
| #define DYNAMIC_PROXY_JS_ERROR -4 | ||
| #ifdef WIN32 | ||
| typedef long threadId; | ||
| #else | ||
| typedef pthread_t threadId; | ||
| #endif | ||
| threadId v8ThreadId; | ||
| std::queue<DynamicProxyJsCallData *> queue_dynamicProxyJsCallData; | ||
| uv_mutex_t uvMutex_dynamicProxyJsCall; | ||
| uv_async_t uvAsync_dynamicProxyJsCall; | ||
| /*static*/ Nan::Persistent<v8::FunctionTemplate> Java::s_ct; | ||
| /*static*/ std::string Java::s_nativeBindingLocation; | ||
| void my_sleep(int dur) { | ||
| #ifdef WIN32 | ||
| Sleep(dur); | ||
| #else | ||
| usleep(dur); | ||
| #endif | ||
| } | ||
| threadId my_getThreadId() { | ||
| #ifdef WIN32 | ||
| return (long)GetCurrentThreadId(); | ||
| #else | ||
| return pthread_self(); | ||
| #endif | ||
| } | ||
| bool v8ThreadIdEquals(threadId a, threadId b) { | ||
| #ifdef WIN32 | ||
| return a == b; | ||
| #else | ||
| return pthread_equal(a, 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<v8::Object> target) { | ||
| Nan::HandleScope scope; | ||
| v8ThreadId = my_getThreadId(); | ||
| uv_mutex_init(&uvMutex_dynamicProxyJsCall); | ||
| uv_async_init(uv_default_loop(), &uvAsync_dynamicProxyJsCall, uvAsyncCb_dynamicProxyJsCall); | ||
| v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New); | ||
| s_ct.Reset(t); | ||
| t->InstanceTemplate()->SetInternalFieldCount(1); | ||
| t->SetClassName(Nan::New<v8::String>("Java").ToLocalChecked()); | ||
| Nan::SetPrototypeMethod(t, "getClassLoader", getClassLoader); | ||
| Nan::SetPrototypeMethod(t, "newInstance", newInstance); | ||
| Nan::SetPrototypeMethod(t, "newInstanceSync", newInstanceSync); | ||
| Nan::SetPrototypeMethod(t, "newProxy", newProxy); | ||
| Nan::SetPrototypeMethod(t, "callStaticMethod", callStaticMethod); | ||
| Nan::SetPrototypeMethod(t, "callStaticMethodSync", callStaticMethodSync); | ||
| Nan::SetPrototypeMethod(t, "callMethod", callMethod); | ||
| Nan::SetPrototypeMethod(t, "callMethodSync", callMethodSync); | ||
| Nan::SetPrototypeMethod(t, "findClassSync", findClassSync); | ||
| Nan::SetPrototypeMethod(t, "newArray", newArray); | ||
| Nan::SetPrototypeMethod(t, "newByte", newByte); | ||
| Nan::SetPrototypeMethod(t, "newShort", newShort); | ||
| Nan::SetPrototypeMethod(t, "newLong", newLong); | ||
| Nan::SetPrototypeMethod(t, "newChar", newChar); | ||
| Nan::SetPrototypeMethod(t, "newFloat", newFloat); | ||
| Nan::SetPrototypeMethod(t, "newDouble", newDouble); | ||
| Nan::SetPrototypeMethod(t, "getStaticFieldValue", getStaticFieldValue); | ||
| Nan::SetPrototypeMethod(t, "setStaticFieldValue", setStaticFieldValue); | ||
| Nan::SetPrototypeMethod(t, "instanceOf", instanceOf); | ||
| Nan::Set(target, Nan::New<v8::String>("Java").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked()); | ||
| JavaProxyObject::init(); | ||
| } | ||
| NAN_METHOD(Java::New) { | ||
| Nan::HandleScope scope; | ||
| Java *self = new Java(); | ||
| self->Wrap(info.This()); | ||
| Nan::Set(self->handle(), Nan::New<v8::String>("classpath").ToLocalChecked(), Nan::New<v8::Array>()); | ||
| Nan::Set(self->handle(), Nan::New<v8::String>("options").ToLocalChecked(), Nan::New<v8::Array>()); | ||
| Nan::Set(self->handle(), Nan::New<v8::String>("nativeBindingLocation").ToLocalChecked(), Nan::New<v8::String>("Not Set").ToLocalChecked()); | ||
| Nan::Set(self->handle(), Nan::New<v8::String>("asyncOptions").ToLocalChecked(), Nan::Null()); | ||
| info.GetReturnValue().Set(info.This()); | ||
| } | ||
| Java::Java() { | ||
| this->m_jvm = NULL; | ||
| this->m_env = NULL; | ||
| m_SyncSuffix = "Sync"; | ||
| m_AsyncSuffix = ""; | ||
| doSync = true; | ||
| doAsync = true; | ||
| doPromise = false; | ||
| } | ||
| Java::~Java() { | ||
| this->destroyJVM(&this->m_jvm, &this->m_env); | ||
| } | ||
| v8::Local<v8::Value> Java::ensureJvm() { | ||
| if(!m_jvm) { | ||
| v8::Local<v8::Value> result = createJVM(&this->m_jvm, &this->m_env); | ||
| assert(result->IsNull()); | ||
| return result; | ||
| } | ||
| return Nan::Null(); | ||
| } | ||
| void Java::configureAsync(v8::Local<v8::Value>& asyncOptions) { | ||
| v8::Local<v8::Object> asyncOptionsObj = asyncOptions.As<v8::Object>(); | ||
| m_SyncSuffix = "invalid"; | ||
| m_AsyncSuffix = "invalid"; | ||
| m_PromiseSuffix = "invalid"; | ||
| doSync = false; | ||
| doAsync = false; | ||
| doPromise = false; | ||
| v8::MaybeLocal<v8::Value> maybeSuffixValue = Nan::Get(asyncOptionsObj, Nan::New<v8::String>("syncSuffix").ToLocalChecked()); | ||
| v8::Local<v8::Value> suffixValue; | ||
| if (maybeSuffixValue.ToLocal(&suffixValue) && suffixValue->IsString()) { | ||
| v8::Local<v8::String> suffix = suffixValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| Nan::Utf8String utf8(suffix); | ||
| m_SyncSuffix.assign(*utf8); | ||
| doSync = true; | ||
| } | ||
| maybeSuffixValue = Nan::Get(asyncOptionsObj, Nan::New<v8::String>("asyncSuffix").ToLocalChecked()); | ||
| if (maybeSuffixValue.ToLocal(&suffixValue) && suffixValue->IsString()) { | ||
| v8::Local<v8::String> suffix = suffixValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| Nan::Utf8String utf8(suffix); | ||
| m_AsyncSuffix.assign(*utf8); | ||
| doAsync = true; | ||
| } | ||
| maybeSuffixValue = Nan::Get(asyncOptionsObj, Nan::New<v8::String>("promiseSuffix").ToLocalChecked()); | ||
| if (maybeSuffixValue.ToLocal(&suffixValue) && suffixValue->IsString()) { | ||
| v8::Local<v8::String> suffix = suffixValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| Nan::Utf8String utf8(suffix); | ||
| m_PromiseSuffix.assign(*utf8); | ||
| v8::MaybeLocal<v8::Value> maybePromisify = Nan::Get(asyncOptionsObj, Nan::New<v8::String>("promisify").ToLocalChecked()); | ||
| v8::Local<v8::Value> promisify; | ||
| if (maybePromisify.ToLocal(&promisify) && !promisify->IsFunction()) { | ||
| fprintf(stderr, "asyncOptions.promisify must be a function"); | ||
| assert(promisify->IsFunction()); | ||
| } | ||
| doPromise = true; | ||
| } | ||
| if (doSync && doAsync) { | ||
| assert(m_SyncSuffix != m_AsyncSuffix); | ||
| } | ||
| if (doSync && doPromise) { | ||
| assert(m_SyncSuffix != m_PromiseSuffix); | ||
| } | ||
| if (doAsync && doPromise) { | ||
| assert(m_AsyncSuffix != m_PromiseSuffix); | ||
| } | ||
| m_asyncOptions.Reset(asyncOptionsObj); | ||
| } | ||
| v8::Local<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) { | ||
| v8::MaybeLocal<v8::Value> maybeAsyncOptions = Nan::Get(this->handle(), Nan::New<v8::String>("asyncOptions").ToLocalChecked()); | ||
| v8::Local<v8::Value> asyncOptions; | ||
| if (maybeAsyncOptions.ToLocal(&asyncOptions) && asyncOptions->IsObject()) { | ||
| configureAsync(asyncOptions); | ||
| } | ||
| // setup classpath | ||
| std::ostringstream classPath; | ||
| classPath << "-Djava.class.path="; | ||
| v8::MaybeLocal<v8::Value> maybeClassPathValue = Nan::Get(this->handle(), Nan::New<v8::String>("classpath").ToLocalChecked()); | ||
| v8::Local<v8::Value> classPathValue; | ||
| if(!maybeClassPathValue.ToLocal(&classPathValue) || !classPathValue->IsArray()) { | ||
| return Nan::TypeError("Classpath must be an array"); | ||
| } | ||
| v8::Local<v8::Array> classPathArrayTemp = v8::Local<v8::Array>::Cast(classPathValue); | ||
| m_classPathArray.Reset(classPathArrayTemp); | ||
| for(uint32_t i=0; i<classPathArrayTemp->Length(); i++) { | ||
| if(i != 0) { | ||
| #ifdef WIN32 | ||
| classPath << ";"; | ||
| #else | ||
| classPath << ":"; | ||
| #endif | ||
| } | ||
| v8::Local<v8::Value> arrayItemValue = classPathArrayTemp->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); | ||
| if(!arrayItemValue->IsString()) { | ||
| return Nan::TypeError("Classpath must only contain strings"); | ||
| } | ||
| v8::Local<v8::String> arrayItem = arrayItemValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| Nan::Utf8String arrayItemStr(arrayItem); | ||
| classPath << *arrayItemStr; | ||
| } | ||
| // set the native binding location | ||
| v8::Local<v8::Value> v8NativeBindingLocation = Nan::Get(this->handle(), Nan::New<v8::String>("nativeBindingLocation").ToLocalChecked()).FromMaybe(v8::Local<v8::Value>()); | ||
| Nan::Utf8String nativeBindingLocationStr(v8NativeBindingLocation); | ||
| s_nativeBindingLocation = *nativeBindingLocationStr; | ||
| // get other options | ||
| v8::Local<v8::Value> optionsValue = Nan::Get(this->handle(), Nan::New<v8::String>("options").ToLocalChecked()).FromMaybe(v8::Local<v8::Value>()); | ||
| if(!optionsValue->IsArray()) { | ||
| return Nan::TypeError("options must be an array"); | ||
| } | ||
| v8::Local<v8::Array> optionsArrayTemp = v8::Local<v8::Array>::Cast(optionsValue); | ||
| m_optionsArray.Reset(optionsArrayTemp); | ||
| // create vm options | ||
| int vmOptionsCount = optionsArrayTemp->Length() + 1; | ||
| JavaVMOption* vmOptions = new JavaVMOption[vmOptionsCount]; | ||
| //printf("classPath: %s\n", classPath.str().c_str()); | ||
| vmOptions[0].optionString = strdup(classPath.str().c_str()); | ||
| for(uint32_t i=0; i<optionsArrayTemp->Length(); i++) { | ||
| v8::Local<v8::Value> arrayItemValue = optionsArrayTemp->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); | ||
| if(!arrayItemValue->IsString()) { | ||
| delete[] vmOptions; | ||
| return Nan::TypeError("options must only contain strings"); | ||
| } | ||
| v8::Local<v8::String> arrayItem = arrayItemValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| Nan::Utf8String arrayItemStr(arrayItem); | ||
| vmOptions[i+1].optionString = strdup(*arrayItemStr); | ||
| } | ||
| JavaVMInitArgs args; | ||
| // The JNI invocation is documented to include a function JNI_GetDefaultJavaVMInitArgs that | ||
| // was formerly called here. But the documentation from Oracle is confusing/contradictory. | ||
| // 1) It claims that the caller must set args.version before calling JNI_GetDefaultJavaVMInitArgs, which | ||
| // we did not do. | ||
| // 2) The sample code provide at the top of the doc doesn't even call JNI_GetDefaultJavaVMInitArgs. | ||
| // 3) The Oracle documentation for Java 6 through Java 8 all contain a comment "Note that in the JDK/JRE, there is no | ||
| // longer any need to call JNI_GetDefaultJavaVMInitArgs." | ||
| // 4) It seems that some platforms don't implement JNI_GetDefaultJavaVMInitArgs, or have | ||
| // marked it deprecated. | ||
| // Omitting the call to JNI_GetDefaultJavaVMInitArgs works fine on Mac and Linux with Java 7 and Java 8. | ||
| // The Oracle documentation is here: | ||
| // http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html | ||
| // http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html | ||
| // http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html | ||
| args.version = JNI_BEST_VERSION; | ||
| // JNI_GetDefaultJavaVMInitArgs(&args); // If this turns out to be necessary, it should be called here. | ||
| args.ignoreUnrecognized = false; | ||
| args.options = vmOptions; | ||
| args.nOptions = vmOptionsCount; | ||
| JavaVM* jvmTemp; | ||
| JNI_CreateJavaVM(&jvmTemp, (void **)env, &args); | ||
| *jvm = jvmTemp; | ||
| delete [] vmOptions; | ||
| m_classLoader = getSystemClassLoader(*env); | ||
| v8::Local<v8::Value> onJvmCreated = Nan::Get(this->handle(), Nan::New<v8::String>("onJvmCreated").ToLocalChecked()).FromMaybe(v8::Local<v8::Value>()); | ||
| // TODO: this handles sets put doesn't prevent modifing the underlying data. So java.classpath.push will still work which is invalid. | ||
| Nan::SetAccessor(this->handle(), Nan::New<v8::String>("classpath").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); | ||
| Nan::SetAccessor(this->handle(), Nan::New<v8::String>("options").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); | ||
| Nan::SetAccessor(this->handle(), Nan::New<v8::String>("nativeBindingLocation").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); | ||
| Nan::SetAccessor(this->handle(), Nan::New<v8::String>("asyncOptions").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); | ||
| Nan::SetAccessor(this->handle(), Nan::New<v8::String>("onJvmCreated").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); | ||
| if (onJvmCreated->IsFunction()) { | ||
| v8::Local<v8::Function> onJvmCreatedFunc = onJvmCreated.As<v8::Function>(); | ||
| v8::Local<v8::Object> context = Nan::New<v8::Object>(); | ||
| Nan::Call(onJvmCreatedFunc, context, 0, NULL); | ||
| } | ||
| return Nan::Null(); | ||
| } | ||
| NAN_GETTER(Java::AccessorProhibitsOverwritingGetter) { | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| Nan::HandleScope scope; | ||
| Nan::Utf8String nameStr(property); | ||
| if(!strcmp("classpath", *nameStr)) { | ||
| info.GetReturnValue().Set(Nan::New(self->m_classPathArray)); | ||
| return; | ||
| } else if(!strcmp("options", *nameStr)) { | ||
| info.GetReturnValue().Set(Nan::New(self->m_optionsArray)); | ||
| return; | ||
| } else if(!strcmp("nativeBindingLocation", *nameStr)) { | ||
| info.GetReturnValue().Set(Nan::New(Java::s_nativeBindingLocation.c_str()).ToLocalChecked()); | ||
| return; | ||
| } else if(!strcmp("asyncOptions", *nameStr)) { | ||
| info.GetReturnValue().Set(Nan::New(self->m_asyncOptions)); | ||
| return; | ||
| } else if(!strcmp("onJvmCreated", *nameStr)) { | ||
| // There is no good reason to get onJvmCreated, so just fall through to error below. | ||
| } | ||
| std::ostringstream errStr; | ||
| errStr << "Invalid call to accessor " << *nameStr; | ||
| info.GetReturnValue().Set(Nan::Error(errStr.str().c_str())); | ||
| } | ||
| NAN_SETTER(Java::AccessorProhibitsOverwritingSetter) { | ||
| Nan::Utf8String nameStr(property); | ||
| std::ostringstream errStr; | ||
| errStr << "Cannot set " << *nameStr << " after calling any other java function."; | ||
| Nan::ThrowError(errStr.str().c_str()); | ||
| } | ||
| void Java::destroyJVM(JavaVM** jvm, JNIEnv** env) { | ||
| (*jvm)->DestroyJavaVM(); | ||
| *jvm = NULL; | ||
| *env = NULL; | ||
| } | ||
| NAN_METHOD(Java::getClassLoader) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| jclass classClazz = env->FindClass("java/lang/ClassLoader"); | ||
| jmethodID class_getClassLoader = env->GetStaticMethodID(classClazz, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); | ||
| jobject classLoader = env->CallStaticObjectMethod(classClazz, class_getClassLoader); | ||
| checkJavaException(env); | ||
| jobject result = env->NewGlobalRef(classLoader); | ||
| info.GetReturnValue().Set(javaToV8(self, env, result)); | ||
| } | ||
| NAN_METHOD(Java::newInstance) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| ARGS_BACK_CALLBACK(); | ||
| // find class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| EXCEPTION_CALL_CALLBACK(self, "Could not find class " << className.c_str()); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // get method | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindConstructor(env, clazz, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, className, true, info, argsStart, argsEnd); | ||
| EXCEPTION_CALL_CALLBACK(self, msg); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // run | ||
| NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback); | ||
| baton->run(); | ||
| END_CALLBACK_FUNCTION("\"Constructor for class '" << className << "' called without a callback did you mean to use the Sync version?\""); | ||
| } | ||
| NAN_METHOD(Java::newInstanceSync) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| // find class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // find method | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindConstructor(env, clazz, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, className, true, info, argsStart, argsEnd); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, msg)); | ||
| } | ||
| // run | ||
| v8::Local<v8::Value> callback = Nan::Null(); | ||
| NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback); | ||
| v8::Local<v8::Value> result = baton->runSync(); | ||
| delete baton; | ||
| if(result->IsNativeError()) { | ||
| return Nan::ThrowError(result); | ||
| } | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(Java::newProxy) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| ARGS_FRONT_STRING(interfaceName); | ||
| ARGS_FRONT_OBJECT(functions); | ||
| DynamicProxyData* dynamicProxyData = new DynamicProxyData(); | ||
| dynamicProxyData->markerStart = DYNAMIC_PROXY_DATA_MARKER_START; | ||
| dynamicProxyData->markerEnd = DYNAMIC_PROXY_DATA_MARKER_END; | ||
| dynamicProxyData->java = self; | ||
| dynamicProxyData->interfaceName = interfaceName; | ||
| dynamicProxyData->functions.Reset(functions); | ||
| // find NodeDynamicProxyClass | ||
| std::string className = "node.NodeDynamicProxyClass"; | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class node/NodeDynamicProxyClass"; | ||
| delete dynamicProxyData; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // find constructor | ||
| jclass objectClazz = env->FindClass("java/lang/Object"); | ||
| jobjectArray methodArgs = env->NewObjectArray(2, objectClazz, NULL); | ||
| env->SetObjectArrayElement(methodArgs, 0, v8ToJava(env, Nan::New<v8::String>(s_nativeBindingLocation.c_str()).ToLocalChecked())); | ||
| env->SetObjectArrayElement(methodArgs, 1, longToJavaLongObj(env, (jlong)dynamicProxyData)); | ||
| jobject method = javaFindConstructor(env, clazz, methodArgs); | ||
| if(method == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find constructor for class node/NodeDynamicProxyClass"; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // create the NodeDynamicProxyClass | ||
| jclass constructorClazz = env->FindClass("java/lang/reflect/Constructor"); | ||
| jmethodID constructor_newInstance = env->GetMethodID(constructorClazz, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| //printf("invoke: %s\n", javaMethodCallToString(env, m_method, constructor_newInstance, m_args).c_str()); | ||
| // run constructor | ||
| jobject dynamicProxy = env->CallObjectMethod(method, constructor_newInstance, methodArgs); | ||
| if(env->ExceptionCheck()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Error creating class"; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| jclass dynamicInterface = javaFindClass(env, interfaceName); | ||
| if(dynamicInterface == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find interface "; | ||
| errStr << interfaceName; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| jclass classClazz = env->FindClass("java/lang/Class"); | ||
| jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL); | ||
| if(classArray == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class array for Proxy"; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| env->SetObjectArrayElement(classArray, 0, dynamicInterface); | ||
| jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;"); | ||
| jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader); | ||
| assertNoException(env); | ||
| if(classLoader == NULL) { | ||
| jclass objectClazz = env->FindClass("java/lang/Object"); | ||
| jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;"); | ||
| jobject jobjClass = env->CallObjectMethod(dynamicProxy, object_getClass); | ||
| checkJavaException(env); | ||
| classLoader = env->CallObjectMethod(jobjClass, class_getClassLoader); | ||
| checkJavaException(env); | ||
| } | ||
| if(classLoader == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not get classloader for Proxy"; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // create proxy instance | ||
| 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;"); | ||
| jobject proxyInstance = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, dynamicProxy); | ||
| if(env->ExceptionCheck()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Error creating java.lang.reflect.Proxy"; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| v8::Local<v8::Value> result = javaToV8(self, env, proxyInstance, dynamicProxyData); | ||
| dynamicProxyData->jsObject.Reset(result); | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(Java::callStaticMethod) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| ARGS_FRONT_STRING(methodName); | ||
| ARGS_BACK_CALLBACK(); | ||
| // find class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| EXCEPTION_CALL_CALLBACK(self, "Could not create class " << className.c_str()); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // find method | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, clazz, methodName, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, methodName, false, info, argsStart, argsEnd); | ||
| EXCEPTION_CALL_CALLBACK(self, msg); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // run | ||
| StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback); | ||
| baton->run(); | ||
| END_CALLBACK_FUNCTION("\"Static method '" << methodName << "' called without a callback did you mean to use the Sync version?\""); | ||
| } | ||
| NAN_METHOD(Java::callStaticMethodSync) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| ARGS_FRONT_STRING(methodName); | ||
| // find class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // find method | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, clazz, methodName, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, methodName, false, info, argsStart, argsEnd); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, msg)); | ||
| } | ||
| // run | ||
| v8::Local<v8::Value> callback = Nan::Null(); | ||
| StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback); | ||
| v8::Local<v8::Value> result = baton->runSync(); | ||
| delete baton; | ||
| if(result->IsNativeError()) { | ||
| Nan::ThrowError(result); | ||
| return; | ||
| } | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(Java::callMethodSync) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_OBJECT(instanceObj); | ||
| ARGS_FRONT_STRING(methodName); | ||
| JavaObject* javaObj = Nan::ObjectWrap::Unwrap<JavaObject>(instanceObj); | ||
| // find method | ||
| jclass clazz = javaObj->getClass(); | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, clazz, methodName, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, methodName, false, info, argsStart, argsEnd); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, msg)); | ||
| } | ||
| // run | ||
| v8::Local<v8::Value> callback = Nan::Null(); | ||
| InstanceMethodCallBaton* baton = new InstanceMethodCallBaton(self, javaObj, method, methodArgs, callback); | ||
| v8::Local<v8::Value> result = baton->runSync(); | ||
| delete baton; | ||
| if(result->IsNativeError()) { | ||
| return Nan::ThrowError(result); | ||
| } | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(Java::callMethod) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_FRONT_OBJECT(instanceObj); | ||
| ARGS_FRONT_STRING(methodName); | ||
| ARGS_BACK_CALLBACK(); | ||
| JavaObject* javaObj = Nan::ObjectWrap::Unwrap<JavaObject>(instanceObj); | ||
| // find method | ||
| jclass clazz = javaObj->getClass(); | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, clazz, methodName, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, clazz, methodName, false, info, argsStart, argsEnd); | ||
| EXCEPTION_CALL_CALLBACK(self, msg); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // run | ||
| InstanceMethodCallBaton* baton = new InstanceMethodCallBaton(self, javaObj, method, methodArgs, callback); | ||
| baton->run(); | ||
| END_CALLBACK_FUNCTION("\"method '" << methodName << "' called without a callback did you mean to use the Sync version?\""); | ||
| } | ||
| NAN_METHOD(Java::findClassSync) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| // find class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // run | ||
| v8::Local<v8::Value> result = javaToV8(self, env, clazz); | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(Java::newArray) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| // argument - array | ||
| if(info.Length() < argsStart+1 || !info[argsStart]->IsArray()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Argument " << (argsStart+1) << " must be an array"; | ||
| return Nan::ThrowError(Nan::TypeError(errStr.str().c_str())); | ||
| } | ||
| v8::Local<v8::Array> arrayObj = v8::Local<v8::Array>::Cast(info[argsStart]); | ||
| // find class and method | ||
| jarray results; | ||
| if(strcmp(className.c_str(), "byte") == 0) { | ||
| results = env->NewByteArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jbyte byteValues[1]; | ||
| byteValues[0] = env->CallByteMethod(val, byte_byteValue); | ||
| assertNoException(env); | ||
| env->SetByteArrayRegion((jbyteArray)results, i, 1, byteValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "char") == 0) { | ||
| results = env->NewCharArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jchar itemValues[1]; | ||
| itemValues[0] = env->CallCharMethod(val, string_charAt, 0); | ||
| checkJavaException(env); | ||
| env->SetCharArrayRegion((jcharArray)results, i, 1, itemValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "short") == 0) { | ||
| results = env->NewShortArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jshort shortValues[1]; | ||
| shortValues[0] = env->CallShortMethod(val, short_shortValue); | ||
| assertNoException(env); | ||
| env->SetShortArrayRegion((jshortArray)results, i, 1, shortValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "double") == 0) { | ||
| results = env->NewDoubleArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jdouble doubleValues[1]; | ||
| doubleValues[0] = env->CallDoubleMethod(val, double_doubleValue); | ||
| assertNoException(env); | ||
| env->SetDoubleArrayRegion((jdoubleArray)results, i, 1, doubleValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "int") == 0) { | ||
| results = env->NewIntArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jint intValues[1]; | ||
| intValues[0] = env->CallIntMethod(val, integer_intValue); | ||
| assertNoException(env); | ||
| env->SetIntArrayRegion((jintArray)results, i, 1, intValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "float") == 0) { | ||
| results = env->NewFloatArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jfloat floatValues[1]; | ||
| floatValues[0] = env->CallFloatMethod(val, float_floatValue); | ||
| checkJavaException(env); | ||
| env->SetFloatArrayRegion((jfloatArray)results, i, 1, floatValues); | ||
| } | ||
| } | ||
| else if(strcmp(className.c_str(), "boolean") == 0) { | ||
| results = env->NewBooleanArray(arrayObj->Length()); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> 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"); | ||
| jboolean booleanValues[1]; | ||
| booleanValues[0] = env->CallBooleanMethod(val, boolean_booleanValue); | ||
| checkJavaException(env); | ||
| env->SetBooleanArrayRegion((jbooleanArray)results, i, 1, booleanValues); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // create array | ||
| results = env->NewObjectArray(arrayObj->Length(), clazz, NULL); | ||
| for(uint32_t i=0; i<arrayObj->Length(); i++) { | ||
| v8::Local<v8::Value> item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); | ||
| jobject val = v8ToJava(env, item); | ||
| env->SetObjectArrayElement((jobjectArray)results, i, val); | ||
| if(env->ExceptionOccurred()) { | ||
| std::ostringstream errStr; | ||
| Nan::Utf8String valStr(item); | ||
| errStr << "Could not add item \"" << *valStr << "\" to array."; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| } | ||
| } | ||
| info.GetReturnValue().Set(JavaObject::New(self, results)); | ||
| } | ||
| NAN_METHOD(Java::newByte) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newByte only takes 1 argument")); | ||
| } | ||
| // argument - value | ||
| if(!info[0]->IsNumber()) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number")); | ||
| } | ||
| jbyte val = Nan::To<int32_t>(info[0]).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Byte"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(B)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, val); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| return; | ||
| } | ||
| NAN_METHOD(Java::newShort) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newShort only takes 1 argument")); | ||
| } | ||
| // argument - value | ||
| if(!info[0]->IsNumber()) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number")); | ||
| } | ||
| jshort val = Nan::To<int32_t>(info[0]).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Short"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(S)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, val); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| } | ||
| NAN_METHOD(Java::newLong) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newLong only takes 1 argument")); | ||
| } | ||
| // argument - value | ||
| if(!info[0]->IsNumber()) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number")); | ||
| } | ||
| jlong val = Nan::To<int64_t>(info[0]).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Long"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(J)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, val); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| } | ||
| NAN_METHOD(Java::newChar) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newChar only takes 1 argument")); | ||
| } | ||
| // argument - value | ||
| jchar charVal; | ||
| if(info[0]->IsNumber()) { | ||
| charVal = (jchar)Nan::To<int32_t>(info[0]).FromJust(); | ||
| } else if(info[0]->IsString()) { | ||
| v8::Local<v8::String> val = info[0]->ToString(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| if(val->Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a string of 1 character.")); | ||
| } | ||
| std::string strVal = std::string(*Nan::Utf8String(val)); | ||
| charVal = (jchar)strVal[0]; | ||
| } else { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number or string")); | ||
| } | ||
| jclass clazz = env->FindClass("java/lang/Character"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(C)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, charVal); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| } | ||
| NAN_METHOD(Java::newFloat) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newFloat only takes 1 argument")); | ||
| } else if(!info[0]->IsNumber()) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number")); | ||
| } | ||
| jfloat val = (jfloat)Nan::To<double>(info[0]).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Float"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(F)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, val); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| } | ||
| NAN_METHOD(Java::newDouble) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| if(info.Length() != 1) { | ||
| return Nan::ThrowError(Nan::TypeError("newDouble only takes 1 argument")); | ||
| } else if(!info[0]->IsNumber()) { | ||
| return Nan::ThrowError(Nan::TypeError("Argument 1 must be a number")); | ||
| } | ||
| jdouble val = (jdouble)Nan::To<double>(info[0]).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Double"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(D)V"); | ||
| jobject newObj = env->NewObject(clazz, constructor, val); | ||
| info.GetReturnValue().Set(JavaObject::New(self, newObj)); | ||
| } | ||
| NAN_METHOD(Java::getStaticFieldValue) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| ARGS_FRONT_STRING(fieldName); | ||
| // find the class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| // get the field | ||
| jobject field = javaFindField(env, clazz, fieldName); | ||
| if(field == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find field \"" << fieldName.c_str() << "\" on class \"" << className.c_str() << "\""; | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_get = env->GetMethodID(fieldClazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| // get field value | ||
| jobject val = env->CallObjectMethod(field, field_get, NULL); | ||
| if(env->ExceptionOccurred()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not get field " << fieldName.c_str() << " on class " << className.c_str(); | ||
| return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| } | ||
| info.GetReturnValue().Set(javaToV8(self, env, val)); | ||
| } | ||
| NAN_METHOD(Java::setStaticFieldValue) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| // arguments | ||
| ARGS_FRONT_CLASSNAME(); | ||
| ARGS_FRONT_STRING(fieldName); | ||
| // argument - new value | ||
| if(info.Length() < argsStart+1) { | ||
| std::ostringstream errStr; | ||
| errStr << "setStaticFieldValue requires " << (argsStart+1) << " arguments"; | ||
| Nan::ThrowError(Nan::TypeError(errStr.str().c_str())); | ||
| return; | ||
| } | ||
| jobject newValue = v8ToJava(env, info[argsStart]); | ||
| argsStart++; | ||
| // find the class | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(clazz == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not create class " << className.c_str(); | ||
| Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| return; | ||
| } | ||
| // get the field | ||
| jobject field = javaFindField(env, clazz, fieldName); | ||
| if(field == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find field \"" << fieldName.c_str() << "\" on class \"" << className.c_str() << "\""; | ||
| Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| return; | ||
| } | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_set = env->GetMethodID(fieldClazz, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); | ||
| //printf("newValue: %s\n", javaObjectToString(env, newValue).c_str()); | ||
| // set field value | ||
| env->CallObjectMethod(field, field_set, NULL, newValue); | ||
| if(env->ExceptionOccurred()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not set field " << fieldName.c_str() << " on class " << className.c_str(); | ||
| Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| return; | ||
| } | ||
| info.GetReturnValue().SetUndefined(); | ||
| } | ||
| NAN_METHOD(Java::instanceOf) { | ||
| Nan::HandleScope scope; | ||
| Java* self = Nan::ObjectWrap::Unwrap<Java>(info.This()); | ||
| v8::Local<v8::Value> ensureJvmResults = self->ensureJvm(); | ||
| if(!ensureJvmResults->IsNull()) { | ||
| info.GetReturnValue().Set(ensureJvmResults); | ||
| return; | ||
| } | ||
| JNIEnv* env = self->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| int argsStart = 0; | ||
| ARGS_FRONT_OBJECT(obj); | ||
| ARGS_FRONT_STRING(className); | ||
| jobject instance = v8ToJava(env, obj); | ||
| if (!instance) { | ||
| // not even a Java object | ||
| info.GetReturnValue().Set(Nan::New<v8::Boolean>(false)); | ||
| return; | ||
| } | ||
| jclass clazz = javaFindClass(env, className); | ||
| if(!clazz) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find class " << className.c_str(); | ||
| Nan::ThrowError(javaExceptionToV8(self, env, errStr.str())); | ||
| return; | ||
| } | ||
| jboolean res = env->IsInstanceOf(instance, clazz); | ||
| info.GetReturnValue().Set(Nan::New<v8::Boolean>(res)); | ||
| } | ||
| template <typename T> | ||
| std::string to_string(T value) { | ||
| std::ostringstream os; | ||
| os << value; | ||
| return os.str(); | ||
| } | ||
| void EIO_CallJs(DynamicProxyJsCallData *callData) { | ||
| DynamicProxyData* dynamicProxyData = callData->dynamicProxyData; | ||
| assert(callData->done == 0); | ||
| if(!dynamicProxyDataVerify(dynamicProxyData)) { | ||
| return; | ||
| } | ||
| callData->result = NULL; | ||
| JNIEnv* env; | ||
| int ret = dynamicProxyData->java->getJvm()->GetEnv((void**)&env, JNI_BEST_VERSION); | ||
| if (ret != JNI_OK) { | ||
| callData->throwableClass = "java/lang/IllegalStateException"; | ||
| callData->throwableMessage = "Could not retrieve JNIEnv: jvm->GetEnv returned " + to_string<int>(ret); | ||
| callData->done = DYNAMIC_PROXY_JS_ERROR; | ||
| return; | ||
| } | ||
| Nan::HandleScope scope; | ||
| v8::Array* v8Args; | ||
| v8::Local<v8::Function> fn; | ||
| v8::Local<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::GetCurrentContext(), Nan::New<v8::String>(callData->methodName.c_str()).ToLocalChecked()).ToLocalChecked(); | ||
| if(fnObj->IsUndefined() || fnObj->IsNull()) { | ||
| callData->throwableClass = "java/lang/NoSuchMethodError"; | ||
| callData->throwableMessage = "Could not find js function " + callData->methodName; | ||
| callData->done = DYNAMIC_PROXY_JS_ERROR; | ||
| return; | ||
| } | ||
| if(!fnObj->IsFunction()) { | ||
| callData->throwableClass = "java/lang/IllegalStateException"; | ||
| callData->throwableMessage = callData->methodName + " is not a function"; | ||
| callData->done = DYNAMIC_PROXY_JS_ERROR; | ||
| return; | ||
| } | ||
| fn = fnObj.As<v8::Function>(); | ||
| if(callData->args) { | ||
| v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, callData->args)); | ||
| argc = v8Args->Length(); | ||
| } else { | ||
| argc = 0; | ||
| } | ||
| argv = new v8::Local<v8::Value>[argc]; | ||
| for(i=0; i<argc; i++) { | ||
| argv[i] = v8Args->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); | ||
| } | ||
| Nan::TryCatch tryCatch; | ||
| tryCatch.SetCaptureMessage(true); | ||
| v8Result = Nan::Call(fn, dynamicProxyDataFunctions, argc, argv).FromMaybe(v8::Local<v8::Value>()); | ||
| delete[] argv; | ||
| if (tryCatch.HasCaught()) { | ||
| callData->throwableClass = "node/NodeJsException"; | ||
| Nan::Utf8String message(tryCatch.Message()->Get()); | ||
| callData->throwableMessage = std::string(*message); | ||
| tryCatch.Reset(); | ||
| callData->done = DYNAMIC_PROXY_JS_ERROR; | ||
| return; | ||
| } | ||
| if(!dynamicProxyDataVerify(dynamicProxyData)) { | ||
| return; | ||
| } | ||
| javaResult = v8ToJava(env, v8Result); | ||
| if(javaResult == NULL) { | ||
| callData->result = NULL; | ||
| } else { | ||
| callData->result = env->NewGlobalRef(javaResult); | ||
| } | ||
| callData->done = true; | ||
| } | ||
| void throwNewThrowable(JNIEnv* env, const char * excClassName, std::string msg) { | ||
| jclass newExcCls = env->FindClass(excClassName); | ||
| jthrowable throwable = env->ExceptionOccurred(); | ||
| if (throwable != NULL) { | ||
| env->Throw(throwable); // this should only be Errors, according to the docs | ||
| } | ||
| env->ThrowNew(newExcCls, msg.c_str()); | ||
| } | ||
| JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jlong ptr, jobject method, jobjectArray args) { | ||
| threadId myThreadId = my_getThreadId(); | ||
| bool hasArgsGlobalRef = false; | ||
| DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; | ||
| // 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;"); | ||
| callData.methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName)); | ||
| assertNoException(env); | ||
| if(v8ThreadIdEquals(myThreadId, v8ThreadId)) { | ||
| 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 | ||
| callData.args = (jobjectArray) env->NewGlobalRef(args); | ||
| hasArgsGlobalRef = true; | ||
| } | ||
| 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(!callData.done) { | ||
| my_sleep(100); | ||
| } | ||
| } | ||
| if(!dynamicProxyDataVerify(dynamicProxyData)) { | ||
| throwNewThrowable(env, "java/lang/IllegalStateException", "dynamicProxyData was corrupted"); | ||
| } | ||
| if(hasArgsGlobalRef) { | ||
| env->DeleteGlobalRef(callData.args); | ||
| } | ||
| if (callData.done == DYNAMIC_PROXY_JS_ERROR) { | ||
| throwNewThrowable(env, callData.throwableClass.c_str(), callData.throwableMessage); | ||
| } | ||
| jobject result = NULL; | ||
| if(callData.result) { | ||
| // need to retain a local ref so that we can return it, otherwise the returned object gets corrupted | ||
| result = env->NewLocalRef(callData.result); | ||
| env->DeleteGlobalRef(callData.result); | ||
| } | ||
| return result; | ||
| } | ||
| JNIEXPORT void JNICALL Java_node_NodeDynamicProxyClass_unref(JNIEnv *env, jobject src, jlong ptr) { | ||
| DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; | ||
| unref(dynamicProxyData); | ||
| } |
+82
| #ifndef _node_java_h_ | ||
| #define _node_java_h_ | ||
| #include <v8.h> | ||
| #include <node.h> | ||
| #include <jni.h> | ||
| #include <string> | ||
| #include <nan.h> | ||
| #ifdef JNI_VERSION_1_8 | ||
| #define JNI_BEST_VERSION JNI_VERSION_1_8 | ||
| #else | ||
| #define JNI_BEST_VERSION JNI_VERSION_1_6 | ||
| #endif | ||
| class Java : public Nan::ObjectWrap { | ||
| public: | ||
| static void Init(v8::Local<v8::Object> target); | ||
| JavaVM* getJvm() { return m_jvm; } | ||
| JNIEnv* getJavaEnv() { return m_env; } // can only be used safely by the main thread as this is the thread it belongs to | ||
| jobject getClassLoader() { return m_classLoader; } | ||
| public: | ||
| bool DoSync() const { return doSync; } | ||
| bool DoAsync() const { return doAsync; } | ||
| bool DoPromise() const { return doPromise; } | ||
| std::string SyncSuffix() const { return m_SyncSuffix; } | ||
| std::string AsyncSuffix() const { return m_AsyncSuffix; } | ||
| std::string PromiseSuffix() const { return m_PromiseSuffix; } | ||
| private: | ||
| Java(); | ||
| ~Java(); | ||
| v8::Local<v8::Value> createJVM(JavaVM** jvm, JNIEnv** env); | ||
| void destroyJVM(JavaVM** jvm, JNIEnv** env); | ||
| void configureAsync(v8::Local<v8::Value>& asyncOptions); | ||
| static NAN_METHOD(New); | ||
| static NAN_METHOD(getClassLoader); | ||
| static NAN_METHOD(newInstance); | ||
| static NAN_METHOD(newInstanceSync); | ||
| static NAN_METHOD(newProxy); | ||
| static NAN_METHOD(callStaticMethod); | ||
| static NAN_METHOD(callStaticMethodSync); | ||
| static NAN_METHOD(callMethod); | ||
| static NAN_METHOD(callMethodSync); | ||
| static NAN_METHOD(findClassSync); | ||
| static NAN_METHOD(newArray); | ||
| static NAN_METHOD(newByte); | ||
| static NAN_METHOD(newChar); | ||
| static NAN_METHOD(newShort); | ||
| static NAN_METHOD(newLong); | ||
| static NAN_METHOD(newFloat); | ||
| static NAN_METHOD(newDouble); | ||
| static NAN_METHOD(getStaticFieldValue); | ||
| static NAN_METHOD(setStaticFieldValue); | ||
| static NAN_METHOD(instanceOf); | ||
| static NAN_GETTER(AccessorProhibitsOverwritingGetter); | ||
| static NAN_SETTER(AccessorProhibitsOverwritingSetter); | ||
| v8::Local<v8::Value> ensureJvm(); | ||
| static Nan::Persistent<v8::FunctionTemplate> s_ct; | ||
| JavaVM* m_jvm; | ||
| JNIEnv* m_env; // can only be used safely by the main thread as this is the thread it belongs to | ||
| jobject m_classLoader; | ||
| std::string m_classPath; | ||
| static std::string s_nativeBindingLocation; | ||
| Nan::Persistent<v8::Array> m_classPathArray; | ||
| Nan::Persistent<v8::Array> m_optionsArray; | ||
| Nan::Persistent<v8::Object> m_asyncOptions; | ||
| std::string m_SyncSuffix; | ||
| std::string m_AsyncSuffix; | ||
| std::string m_PromiseSuffix; | ||
| bool doSync; | ||
| bool doAsync; | ||
| bool doPromise; | ||
| }; | ||
| #endif |
| #include "javaObject.h" | ||
| #include "java.h" | ||
| #include "javaScope.h" | ||
| #include "utils.h" | ||
| #include <sstream> | ||
| #include <algorithm> | ||
| /*static*/ std::map<std::string, Nan::Persistent<v8::FunctionTemplate>*> JavaObject::sFunctionTemplates; | ||
| /*static*/ void JavaObject::Init(v8::Local<v8::Object> target) { | ||
| } | ||
| /*static*/ v8::Local<v8::Object> JavaObject::New(Java *java, jobject obj) { | ||
| Nan::EscapableHandleScope scope; | ||
| JNIEnv *env = java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| jclass objClazz = env->GetObjectClass(obj); | ||
| jclass classClazz = env->FindClass("java/lang/Class"); | ||
| jmethodID class_getName = env->GetMethodID(classClazz, "getName", "()Ljava/lang/String;"); | ||
| jobject classNameJava = env->CallObjectMethod(objClazz, class_getName); | ||
| checkJavaException(env); | ||
| std::string className = javaObjectToString(env, classNameJava); | ||
| std::replace(className.begin(), className.end(), '.', '_'); | ||
| std::replace(className.begin(), className.end(), '$', '_'); | ||
| std::replace(className.begin(), className.end(), '[', 'a'); | ||
| className = "nodeJava_" + className; | ||
| v8::Local<v8::Function> promisify; | ||
| if(java->DoPromise()) { | ||
| v8::Local<v8::Object> asyncOptions = java->handle()->Get(Nan::GetCurrentContext(), Nan::New<v8::String>("asyncOptions").ToLocalChecked()).ToLocalChecked().As<v8::Object>(); | ||
| v8::Local<v8::Value> promisifyValue = asyncOptions->Get(Nan::GetCurrentContext(), Nan::New<v8::String>("promisify").ToLocalChecked()).ToLocalChecked(); | ||
| promisify = promisifyValue.As<v8::Function>(); | ||
| } | ||
| v8::Local<v8::FunctionTemplate> funcTemplate; | ||
| if(sFunctionTemplates.find(className) != sFunctionTemplates.end()) { | ||
| //printf("existing className: %s\n", className.c_str()); | ||
| funcTemplate = Nan::New(*sFunctionTemplates[className]); | ||
| } else { | ||
| //printf("create className: %s\n", className.c_str()); | ||
| funcTemplate = Nan::New<v8::FunctionTemplate>(); | ||
| funcTemplate->InstanceTemplate()->SetInternalFieldCount(1); | ||
| funcTemplate->SetClassName(Nan::New<v8::String>(className.c_str()).ToLocalChecked()); | ||
| // copy methods to template | ||
| std::list<jobject> methods; | ||
| javaReflectionGetMethods(env, objClazz, &methods, false); | ||
| jclass methodClazz = env->FindClass("java/lang/reflect/Method"); | ||
| jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;"); | ||
| for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); ++it) { | ||
| jstring methodNameJava = (jstring)env->CallObjectMethod(*it, method_getName); | ||
| assertNoException(env); | ||
| std::string methodNameStr = javaToString(env, methodNameJava); | ||
| v8::Local<v8::String> baseMethodName = Nan::New<v8::String>(methodNameStr.c_str()).ToLocalChecked(); | ||
| std::string methodNameAsyncStr = methodNameStr; | ||
| const char* methodNameAsync = methodNameAsyncStr.append(java->AsyncSuffix()).c_str(); | ||
| v8::Local<v8::FunctionTemplate> methodCallTemplate = Nan::New<v8::FunctionTemplate>(methodCall, baseMethodName); | ||
| Nan::SetPrototypeTemplate(funcTemplate, methodNameAsync, methodCallTemplate); | ||
| std::string methodNameSyncStr = methodNameStr; | ||
| const char* methodNameSync = methodNameSyncStr.append(java->SyncSuffix()).c_str(); | ||
| v8::Local<v8::FunctionTemplate> methodCallSyncTemplate = Nan::New<v8::FunctionTemplate>(methodCallSync, baseMethodName); | ||
| Nan::SetPrototypeTemplate(funcTemplate, methodNameSync, methodCallSyncTemplate); | ||
| if (java->DoPromise()) { | ||
| v8::Local<v8::Object> recv = Nan::New<v8::Object>(); | ||
| v8::Local<v8::Value> argv[] = { methodCallTemplate->GetFunction(Nan::GetCurrentContext()).ToLocalChecked() }; | ||
| v8::Local<v8::Value> result = Nan::Call(promisify, recv, 1, argv).FromMaybe(v8::Local<v8::Value>()); | ||
| if (!result->IsFunction()) { | ||
| fprintf(stderr, "Promisified result is not a function -- asyncOptions.promisify must return a function.\n"); | ||
| assert(result->IsFunction()); | ||
| } | ||
| v8::Local<v8::Function> promFunction = result.As<v8::Function>(); | ||
| v8::Local<v8::FunctionTemplate> promFunctionTemplate = Nan::New<v8::FunctionTemplate>(methodCallPromise, promFunction); | ||
| std::string methodNamePromiseStr = methodNameStr; | ||
| const char* methodNamePromise = methodNamePromiseStr.append(java->PromiseSuffix()).c_str(); | ||
| Nan::SetPrototypeTemplate(funcTemplate, methodNamePromise, promFunctionTemplate); | ||
| } | ||
| } | ||
| // copy fields to template | ||
| std::list<jobject> fields; | ||
| javaReflectionGetFields(env, objClazz, &fields); | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_getName = env->GetMethodID(fieldClazz, "getName", "()Ljava/lang/String;"); | ||
| for(std::list<jobject>::iterator it = fields.begin(); it != fields.end(); ++it) { | ||
| jstring fieldNameJava = (jstring)env->CallObjectMethod(*it, field_getName); | ||
| checkJavaException(env); | ||
| std::string fieldNameStr = javaToString(env, fieldNameJava); | ||
| v8::Local<v8::String> fieldName = Nan::New<v8::String>(fieldNameStr.c_str()).ToLocalChecked(); | ||
| Nan::SetAccessor(funcTemplate->InstanceTemplate(), fieldName, fieldGetter, fieldSetter); | ||
| } | ||
| // copy array methods to template | ||
| jmethodID class_isArray = env->GetMethodID(classClazz, "isArray", "()Z"); | ||
| jboolean isArray = env->CallBooleanMethod(objClazz, class_isArray); | ||
| if(isArray) { | ||
| v8::Local<v8::String> fieldName = Nan::New<v8::String>("length").ToLocalChecked(); | ||
| Nan::SetAccessor(funcTemplate->InstanceTemplate(), fieldName, fieldGetter, NULL); | ||
| Nan::SetIndexedPropertyHandler(funcTemplate->InstanceTemplate(), indexGetter); | ||
| } | ||
| Nan::Persistent<v8::FunctionTemplate>* persistentFuncTemplate = new Nan::Persistent<v8::FunctionTemplate>(); | ||
| persistentFuncTemplate->Reset(funcTemplate); | ||
| sFunctionTemplates[className] = persistentFuncTemplate; | ||
| } | ||
| v8::Local<v8::Function> ctor = Nan::GetFunction(funcTemplate).ToLocalChecked(); | ||
| v8::Local<v8::Object> javaObjectObj = Nan::NewInstance(ctor).ToLocalChecked(); | ||
| SetHiddenValue(javaObjectObj, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked(), Nan::New<v8::Boolean>(true)); | ||
| JavaObject *self = new JavaObject(java, obj); | ||
| self->Wrap(javaObjectObj); | ||
| return scope.Escape(javaObjectObj); | ||
| } | ||
| JavaObject::JavaObject(Java *java, jobject obj) { | ||
| m_java = java; | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| m_obj = env->NewGlobalRef(obj); | ||
| m_class = (jclass)env->NewGlobalRef(env->GetObjectClass(obj)); | ||
| } | ||
| JavaObject::~JavaObject() { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| env->DeleteGlobalRef(m_obj); | ||
| env->DeleteGlobalRef(m_class); | ||
| } | ||
| NAN_METHOD(JavaObject::methodCall) { | ||
| Nan::HandleScope scope; | ||
| JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This()); | ||
| JNIEnv *env = self->m_java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| Nan::Utf8String methodName(info.Data()); | ||
| std::string methodNameStr = *methodName; | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| // arguments | ||
| ARGS_BACK_CALLBACK(); | ||
| if(!callbackProvided && methodNameStr == "toString") { | ||
| return methodCallSync(info); | ||
| } | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, self->m_class, methodNameStr, false, info, argsStart, argsEnd); | ||
| EXCEPTION_CALL_CALLBACK(self->m_java, msg); | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| // run | ||
| InstanceMethodCallBaton* baton = new InstanceMethodCallBaton(self->m_java, self, method, methodArgs, callback); | ||
| baton->run(); | ||
| END_CALLBACK_FUNCTION("\"Method '" << methodNameStr << "' called without a callback did you mean to use the Sync version?\""); | ||
| } | ||
| NAN_METHOD(JavaObject::methodCallSync) { | ||
| Nan::HandleScope scope; | ||
| JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This()); | ||
| JNIEnv *env = self->m_java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| Nan::Utf8String methodName(info.Data()); | ||
| std::string methodNameStr = *methodName; | ||
| int argsStart = 0; | ||
| int argsEnd = info.Length(); | ||
| jobjectArray methodArgs = v8ToJava(env, info, argsStart, argsEnd); | ||
| jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs); | ||
| if(method == NULL) { | ||
| std::string msg = methodNotFoundToString(env, self->m_class, methodNameStr, false, info, argsStart, argsEnd); | ||
| v8::Local<v8::Value> ex = javaExceptionToV8(self->m_java, env, msg); | ||
| Nan::ThrowError(ex); | ||
| return; | ||
| } | ||
| // run | ||
| v8::Local<v8::Value> callback = Nan::Undefined(); | ||
| InstanceMethodCallBaton* baton = new InstanceMethodCallBaton(self->m_java, self, method, methodArgs, callback); | ||
| v8::Local<v8::Value> result = baton->runSync(); | ||
| delete baton; | ||
| if(result->IsNativeError()) { | ||
| Nan::ThrowError(result); | ||
| return; | ||
| } | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_METHOD(JavaObject::methodCallPromise) { | ||
| Nan::HandleScope scope; | ||
| v8::Local<v8::Function> fn = info.Data().As<v8::Function>(); | ||
| v8::Local<v8::Value>* argv = new v8::Local<v8::Value>[info.Length()]; | ||
| for (int i = 0 ; i < info.Length(); i++) { | ||
| argv[i] = info[i]; | ||
| } | ||
| v8::MaybeLocal<v8::Value> result = Nan::Call(fn, info.This(), info.Length(), argv); | ||
| delete[] argv; | ||
| if (!result.IsEmpty()) { | ||
| info.GetReturnValue().Set(result.ToLocalChecked()); | ||
| } | ||
| } | ||
| NAN_GETTER(JavaObject::fieldGetter) { | ||
| Nan::HandleScope scope; | ||
| JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This()); | ||
| JNIEnv *env = self->m_java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| Nan::Utf8String propertyCStr(property); | ||
| std::string propertyStr = *propertyCStr; | ||
| jobject field = javaFindField(env, self->m_class, propertyStr); | ||
| if(field == NULL) { | ||
| if(propertyStr == "length") { | ||
| jclass classClazz = env->FindClass("java/lang/Class"); | ||
| jmethodID class_isArray = env->GetMethodID(classClazz, "isArray", "()Z"); | ||
| jboolean isArray = env->CallBooleanMethod(self->m_class, class_isArray); | ||
| if(isArray) { | ||
| jclass arrayClass = env->FindClass("java/lang/reflect/Array"); | ||
| jmethodID array_getLength = env->GetStaticMethodID(arrayClass, "getLength", "(Ljava/lang/Object;)I"); | ||
| jint arrayLength = env->CallStaticIntMethod(arrayClass, array_getLength, self->m_obj); | ||
| assertNoException(env); | ||
| info.GetReturnValue().Set(arrayLength); | ||
| return; | ||
| } | ||
| } | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find field \"" << propertyStr << "\" for get"; | ||
| v8::Local<v8::Value> ex = javaExceptionToV8(self->m_java, env, errStr.str()); | ||
| Nan::ThrowError(ex); | ||
| return; | ||
| } | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_get = env->GetMethodID(fieldClazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| // get field value | ||
| jobject val = env->CallObjectMethod(field, field_get, self->m_obj); | ||
| if(env->ExceptionOccurred()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not get field " << propertyStr; | ||
| v8::Local<v8::Value> ex = javaExceptionToV8(self->m_java, env, errStr.str()); | ||
| Nan::ThrowError(ex); | ||
| return; | ||
| } | ||
| v8::Local<v8::Value> result = javaToV8(self->m_java, env, val); | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| NAN_SETTER(JavaObject::fieldSetter) { | ||
| Nan::HandleScope scope; | ||
| JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This()); | ||
| JNIEnv *env = self->m_java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| jobject newValue = v8ToJava(env, value); | ||
| Nan::Utf8String propertyCStr(property); | ||
| std::string propertyStr = *propertyCStr; | ||
| jobject field = javaFindField(env, self->m_class, propertyStr); | ||
| if(field == NULL) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not find field \"" << propertyStr << "\" for set"; | ||
| v8::Local<v8::Value> error = javaExceptionToV8(self->m_java, env, errStr.str()); | ||
| Nan::ThrowError(error); | ||
| return; | ||
| } | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_set = env->GetMethodID(fieldClazz, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); | ||
| //printf("newValue: %s\n", javaObjectToString(env, newValue).c_str()); | ||
| // set field value | ||
| env->CallObjectMethod(field, field_set, self->m_obj, newValue); | ||
| if(env->ExceptionOccurred()) { | ||
| std::ostringstream errStr; | ||
| errStr << "Could not set field " << propertyStr; | ||
| v8::Local<v8::Value> error = javaExceptionToV8(self->m_java, env, errStr.str()); | ||
| Nan::ThrowError(error); | ||
| return; | ||
| } | ||
| } | ||
| NAN_INDEX_GETTER(JavaObject::indexGetter) { | ||
| Nan::HandleScope scope; | ||
| JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This()); | ||
| JNIEnv *env = self->m_java->getJavaEnv(); | ||
| JavaScope javaScope(env); | ||
| jclass arrayClass = env->FindClass("java/lang/reflect/Array"); | ||
| jmethodID array_getLength = env->GetStaticMethodID(arrayClass, "getLength", "(Ljava/lang/Object;)I"); | ||
| jint arrayLength = env->CallStaticIntMethod(arrayClass, array_getLength, self->m_obj); | ||
| assertNoException(env); | ||
| if ((jint)index >= arrayLength) { | ||
| info.GetReturnValue().SetUndefined(); | ||
| return; | ||
| } | ||
| jmethodID array_get = env->GetStaticMethodID(arrayClass, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;"); | ||
| jobject item = env->CallStaticObjectMethod(arrayClass, array_get, self->m_obj, index); | ||
| assertNoException(env); | ||
| v8::Local<v8::Value> result = javaToV8(self->m_java, env, item); | ||
| info.GetReturnValue().Set(result); | ||
| } | ||
| /*static*/ Nan::Persistent<v8::FunctionTemplate> JavaProxyObject::s_proxyCt; | ||
| /*static*/ void JavaProxyObject::init() { | ||
| v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(); | ||
| s_proxyCt.Reset(t); | ||
| t->InstanceTemplate()->SetInternalFieldCount(1); | ||
| t->SetClassName(Nan::New<v8::String>("NodeDynamicProxy").ToLocalChecked()); | ||
| Nan::SetPrototypeTemplate(t, "unref", Nan::New<v8::FunctionTemplate>(doUnref)); | ||
| v8::Local<v8::String> fieldName = Nan::New<v8::String>("invocationHandler").ToLocalChecked(); | ||
| Nan::SetAccessor(t->InstanceTemplate(), fieldName, invocationHandlerGetter); | ||
| } | ||
| v8::Local<v8::Object> JavaProxyObject::New(Java *java, jobject obj, DynamicProxyData* dynamicProxyData) { | ||
| Nan::EscapableHandleScope scope; | ||
| v8::Local<v8::Function> ctor = Nan::New(s_proxyCt)->GetFunction(Nan::GetCurrentContext()).ToLocalChecked(); | ||
| v8::Local<v8::Object> javaObjectObj = Nan::NewInstance(ctor).ToLocalChecked(); | ||
| SetHiddenValue(javaObjectObj, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked(), Nan::New<v8::Boolean>(true)); | ||
| JavaProxyObject *self = new JavaProxyObject(java, obj, dynamicProxyData); | ||
| self->Wrap(javaObjectObj); | ||
| return scope.Escape(javaObjectObj); | ||
| } | ||
| JavaProxyObject::JavaProxyObject(Java *java, jobject obj, DynamicProxyData* dynamicProxyData) : JavaObject(java, obj) { | ||
| m_dynamicProxyData = dynamicProxyData; | ||
| } | ||
| JavaProxyObject::~JavaProxyObject() { | ||
| if(dynamicProxyDataVerify(m_dynamicProxyData)) { | ||
| unref(m_dynamicProxyData); | ||
| } | ||
| } | ||
| NAN_METHOD(JavaProxyObject::doUnref) { | ||
| JavaProxyObject* self = Nan::ObjectWrap::Unwrap<JavaProxyObject>(info.This()); | ||
| if (dynamicProxyDataVerify(self->m_dynamicProxyData)) { | ||
| unref(self->m_dynamicProxyData); | ||
| } | ||
| info.GetReturnValue().SetUndefined(); | ||
| } | ||
| NAN_GETTER(JavaProxyObject::invocationHandlerGetter) { | ||
| Nan::HandleScope scope; | ||
| JavaProxyObject* self = Nan::ObjectWrap::Unwrap<JavaProxyObject>(info.This()); | ||
| if (!dynamicProxyDataVerify(self->m_dynamicProxyData)) { | ||
| Nan::ThrowError("dynamicProxyData has been destroyed or corrupted"); | ||
| return; | ||
| } | ||
| info.GetReturnValue().Set(Nan::New(self->m_dynamicProxyData->functions)); | ||
| } |
| #ifndef _javaobject_h_ | ||
| #define _javaobject_h_ | ||
| #include <v8.h> | ||
| #include <node.h> | ||
| #include <jni.h> | ||
| #include <list> | ||
| #include <map> | ||
| #include "methodCallBaton.h" | ||
| class Java; | ||
| class JavaObject : public Nan::ObjectWrap { | ||
| public: | ||
| static void Init(v8::Local<v8::Object> target); | ||
| static v8::Local<v8::Object> New(Java* java, jobject obj); | ||
| static v8::Local<v8::Object> NewProxy(Java* java, jobject obj, DynamicProxyData* dynamicProxyData); | ||
| jobject getObject() { return m_obj; } | ||
| jclass getClass() { return m_class; } | ||
| void Ref() { Nan::ObjectWrap::Ref(); } | ||
| void Unref() { Nan::ObjectWrap::Unref(); } | ||
| protected: | ||
| JavaObject(Java* java, jobject obj); | ||
| ~JavaObject(); | ||
| private: | ||
| static NAN_METHOD(methodCall); | ||
| static NAN_METHOD(methodCallSync); | ||
| static NAN_METHOD(methodCallPromise); | ||
| static NAN_GETTER(fieldGetter); | ||
| static NAN_SETTER(fieldSetter); | ||
| static NAN_INDEX_GETTER(indexGetter); | ||
| static std::map<std::string, Nan::Persistent<v8::FunctionTemplate>*> sFunctionTemplates; | ||
| Java* m_java; | ||
| jobject m_obj; | ||
| jclass m_class; | ||
| }; | ||
| class JavaProxyObject : public JavaObject { | ||
| public: | ||
| static void init(); | ||
| static v8::Local<v8::Object> New(Java* java, jobject obj, DynamicProxyData* dynamicProxyData); | ||
| private: | ||
| JavaProxyObject(Java* java, jobject obj, DynamicProxyData* dynamicProxyData); | ||
| ~JavaProxyObject(); | ||
| static NAN_METHOD(doUnref); | ||
| static NAN_GETTER(invocationHandlerGetter); | ||
| static Nan::Persistent<v8::FunctionTemplate> s_proxyCt; | ||
| DynamicProxyData* m_dynamicProxyData; | ||
| }; | ||
| #endif | ||
| #include "javaScope.h" | ||
| JavaScope::JavaScope(JNIEnv *env) { | ||
| m_env = env; | ||
| m_result = NULL; | ||
| m_env->PushLocalFrame(LOCAL_FRAME_SIZE); | ||
| } | ||
| JavaScope::~JavaScope() { | ||
| m_env->PopLocalFrame(m_result); | ||
| } | ||
| jobject JavaScope::Close(jobject result) { | ||
| m_result = result; | ||
| return m_result; | ||
| } |
| #ifndef _javaScope_h_ | ||
| #define _javaScope_h_ | ||
| #include <jni.h> | ||
| #define LOCAL_FRAME_SIZE 500 | ||
| class JavaScope { | ||
| public: | ||
| JavaScope(JNIEnv *env); | ||
| ~JavaScope(); | ||
| jobject Close(jobject result); | ||
| private: | ||
| JNIEnv *m_env; | ||
| jobject m_result; | ||
| }; | ||
| #endif |
| #include "methodCallBaton.h" | ||
| #include "java.h" | ||
| #include "javaObject.h" | ||
| #include "javaScope.h" | ||
| jmethodID MethodCallBaton::m_methodInvokeMethodId = 0; | ||
| Nan::Callback* toNanCallback(v8::Local<v8::Value>& callback) { | ||
| if(callback->IsFunction()) { | ||
| return new Nan::Callback(callback.As<v8::Function>()); | ||
| } | ||
| return NULL; | ||
| } | ||
| MethodCallBaton::MethodCallBaton(Java* java, jobject method, jarray args, v8::Local<v8::Value>& callback) : | ||
| Nan::AsyncWorker(toNanCallback(callback)) { | ||
| JNIEnv *env = java->getJavaEnv(); | ||
| m_java = java; | ||
| m_args = (jarray)env->NewGlobalRef(args); | ||
| m_method = env->NewGlobalRef(method); | ||
| m_error = NULL; | ||
| m_result = NULL; | ||
| } | ||
| MethodCallBaton::~MethodCallBaton() { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| if(m_result) { | ||
| env->DeleteGlobalRef(m_result); | ||
| m_result = NULL; | ||
| } | ||
| if(m_error) { | ||
| env->DeleteGlobalRef(m_error); | ||
| m_error = NULL; | ||
| } | ||
| env->DeleteGlobalRef(m_args); | ||
| m_args = NULL; | ||
| env->DeleteGlobalRef(m_method); | ||
| m_method = NULL; | ||
| } | ||
| jmethodID MethodCallBaton::getMethodInvokeMethodId(JNIEnv *env) { | ||
| if(m_methodInvokeMethodId == 0) { | ||
| jclass methodClazz = env->FindClass("java/lang/reflect/Method"); | ||
| m_methodInvokeMethodId = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| } | ||
| return m_methodInvokeMethodId; | ||
| } | ||
| void MethodCallBaton::run() { | ||
| Nan::AsyncQueueWorker(this); | ||
| } | ||
| v8::Local<v8::Value> MethodCallBaton::runSync() { | ||
| JNIEnv* env = m_java->getJavaEnv(); | ||
| ExecuteInternal(env); | ||
| return resultsToV8(env); | ||
| } | ||
| // called by NanAsyncWorker. This will be on a worker thread | ||
| void MethodCallBaton::Execute() { | ||
| JNIEnv* env = javaGetEnv(this->m_java->getJvm(), this->m_java->getClassLoader()); | ||
| JavaScope javaScope(env); | ||
| ExecuteInternal(env); | ||
| } | ||
| // callback from NanAsyncWorker. This will be on the v8 main thread | ||
| void MethodCallBaton::WorkComplete() { | ||
| Nan::HandleScope scope; | ||
| if(callback) { | ||
| JNIEnv* env = javaGetEnv(this->m_java->getJvm(), this->m_java->getClassLoader()); | ||
| JavaScope javaScope(env); | ||
| v8::Local<v8::Value> result = resultsToV8(env); | ||
| if (result->IsNativeError()) { | ||
| v8::Local<v8::Value> argv[] = { | ||
| result | ||
| }; | ||
| callback->Call(1, argv, async_resource); | ||
| } else { | ||
| v8::Local<v8::Value> argv[] = { | ||
| Nan::Undefined(), | ||
| result | ||
| }; | ||
| callback->Call(2, argv, async_resource); | ||
| } | ||
| delete callback; | ||
| callback = NULL; | ||
| } | ||
| } | ||
| v8::Local<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) { | ||
| Nan::EscapableHandleScope scope; | ||
| if(m_error) { | ||
| jthrowable cause = m_error; | ||
| // if we've caught an InvocationTargetException exception, | ||
| // let's grab the cause. users don't necessarily know that | ||
| // we're invoking the methods through reflection | ||
| jclass invocationExceptionClazz = env->FindClass("java/lang/reflect/InvocationTargetException"); | ||
| if (env->IsInstanceOf(m_error, invocationExceptionClazz)) { | ||
| jclass throwableClazz = env->FindClass("java/lang/Throwable"); | ||
| jmethodID throwable_getCause = env->GetMethodID(throwableClazz, "getCause", "()Ljava/lang/Throwable;"); | ||
| cause = (jthrowable)env->CallObjectMethod(m_error, throwable_getCause); | ||
| checkJavaException(env); | ||
| } | ||
| v8::Local<v8::Value> err = javaExceptionToV8(m_java, env, cause, m_errorString); | ||
| return scope.Escape(err); | ||
| } | ||
| return scope.Escape(javaToV8(m_java, env, m_result)); | ||
| } | ||
| void NewInstanceBaton::ExecuteInternal(JNIEnv* env) { | ||
| jclass batonClazz = env->FindClass("node/MethodCallBaton"); | ||
| jmethodID newInstance = env->GetStaticMethodID(batonClazz, "newInstance", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| jarray args = javaGetArgsForConstructor(env, m_method, m_args); | ||
| jobject result = env->CallStaticObjectMethod(batonClazz, newInstance, m_method, args); | ||
| if(env->ExceptionCheck()) { | ||
| jthrowable ex = env->ExceptionOccurred(); | ||
| env->ExceptionClear(); | ||
| m_error = (jthrowable)env->NewGlobalRef(ex); | ||
| m_errorString = "Error creating class"; | ||
| return; | ||
| } | ||
| m_result = env->NewGlobalRef(result); | ||
| } | ||
| void StaticMethodCallBaton::ExecuteInternal(JNIEnv* env) { | ||
| /* | ||
| printf("calling %s\n", javaObjectToString(env, m_method).c_str()); | ||
| printf("arguments\n"); | ||
| for(int i=0; i<env->GetArrayLength(m_args); i++) { | ||
| jobject o = env->GetObjectArrayElement((jobjectArray)m_args, i); | ||
| jclass c = env->GetObjectClass(o); | ||
| printf(" %s (%s)\n", javaObjectToString(env, o).c_str(), javaObjectToString(env, c).c_str()); | ||
| } | ||
| */ | ||
| jclass batonClazz = env->FindClass("node/MethodCallBaton"); | ||
| jmethodID invokeMethod = env->GetStaticMethodID(batonClazz, env->GetVersion() >= 0x90000 ? "invokeMethod9" : "invokeMethod", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| jarray args = javaGetArgsForMethod(env, m_method, m_args); | ||
| jobject result = env->CallStaticObjectMethod(batonClazz, invokeMethod, m_method, NULL, args); | ||
| if(env->ExceptionCheck()) { | ||
| jthrowable ex = env->ExceptionOccurred(); | ||
| env->ExceptionClear(); | ||
| m_error = (jthrowable)env->NewGlobalRef(ex); | ||
| m_errorString = "Error running static method"; | ||
| return; | ||
| } | ||
| m_result = env->NewGlobalRef(result); | ||
| } | ||
| void InstanceMethodCallBaton::ExecuteInternal(JNIEnv* env) { | ||
| /* | ||
| printf("calling %s\n", javaObjectToString(env, m_method).c_str()); | ||
| printf("arguments\n"); | ||
| for(int i=0; i<env->GetArrayLength(m_args); i++) { | ||
| printf(" %s\n", javaObjectToString(env, env->GetObjectArrayElement((jobjectArray)m_args, i)).c_str()); | ||
| } | ||
| */ | ||
| jclass batonClazz = env->FindClass("node/MethodCallBaton"); | ||
| jmethodID invokeMethod = env->GetStaticMethodID(batonClazz, env->GetVersion() >= 0x90000 ? "invokeMethod9" : "invokeMethod", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); | ||
| jarray args = javaGetArgsForMethod(env, m_method, m_args); | ||
| jobject result = env->CallStaticObjectMethod(batonClazz, invokeMethod, m_method, m_javaObject->getObject(), args); | ||
| if(env->ExceptionCheck()) { | ||
| jthrowable ex = env->ExceptionOccurred(); | ||
| env->ExceptionClear(); | ||
| m_error = (jthrowable)env->NewGlobalRef(ex); | ||
| m_errorString = "Error running instance method"; | ||
| return; | ||
| } | ||
| if(result == NULL) { | ||
| m_result = NULL; | ||
| } else { | ||
| m_result = env->NewGlobalRef(result); | ||
| } | ||
| } | ||
| NewInstanceBaton::NewInstanceBaton( | ||
| Java* java, | ||
| jclass clazz, | ||
| jobject method, | ||
| jarray args, | ||
| v8::Local<v8::Value>& callback) : MethodCallBaton(java, method, args, callback) { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| m_clazz = (jclass)env->NewGlobalRef(clazz); | ||
| } | ||
| NewInstanceBaton::~NewInstanceBaton() { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| env->DeleteGlobalRef(m_clazz); | ||
| m_clazz = NULL; | ||
| } | ||
| StaticMethodCallBaton::StaticMethodCallBaton( | ||
| Java* java, | ||
| jclass clazz, | ||
| jobject method, | ||
| jarray args, | ||
| v8::Local<v8::Value>& callback) : MethodCallBaton(java, method, args, callback) { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| m_clazz = (jclass)env->NewGlobalRef(clazz); | ||
| } | ||
| StaticMethodCallBaton::~StaticMethodCallBaton() { | ||
| JNIEnv *env = m_java->getJavaEnv(); | ||
| env->DeleteGlobalRef(m_clazz); | ||
| m_clazz = NULL; | ||
| } | ||
| InstanceMethodCallBaton::InstanceMethodCallBaton( | ||
| Java* java, | ||
| JavaObject* obj, | ||
| jobject method, | ||
| jarray args, | ||
| v8::Local<v8::Value>& callback) : MethodCallBaton(java, method, args, callback) { | ||
| m_javaObject = obj; | ||
| m_javaObject->Ref(); | ||
| } | ||
| InstanceMethodCallBaton::~InstanceMethodCallBaton() { | ||
| if (m_javaObject) { | ||
| m_javaObject->Unref(); | ||
| m_javaObject = NULL; | ||
| } | ||
| } |
| #ifndef _methodcallbaton_h_ | ||
| #define _methodcallbaton_h_ | ||
| #include "utils.h" | ||
| #include <v8.h> | ||
| #include <node.h> | ||
| #include <node_version.h> | ||
| #include <jni.h> | ||
| #include <list> | ||
| class Java; | ||
| class JavaObject; | ||
| class MethodCallBaton : public Nan::AsyncWorker { | ||
| public: | ||
| MethodCallBaton(Java* java, jobject method, jarray args, v8::Local<v8::Value>& callback); | ||
| virtual ~MethodCallBaton(); | ||
| void run(); | ||
| v8::Local<v8::Value> runSync(); | ||
| protected: | ||
| v8::Local<v8::Value> resultsToV8(JNIEnv *env); | ||
| virtual void Execute(); | ||
| virtual void WorkComplete(); | ||
| virtual void ExecuteInternal(JNIEnv* env) = 0; | ||
| static jmethodID getMethodInvokeMethodId(JNIEnv *env); | ||
| Java* m_java; | ||
| jthrowable m_error; | ||
| std::string m_errorString; | ||
| jarray m_args; | ||
| jobject m_result; | ||
| jobject m_method; | ||
| private: | ||
| static jmethodID m_methodInvokeMethodId; | ||
| }; | ||
| class InstanceMethodCallBaton : public MethodCallBaton { | ||
| public: | ||
| InstanceMethodCallBaton(Java* java, JavaObject* obj, jobject method, jarray args, v8::Local<v8::Value>& callback); | ||
| virtual ~InstanceMethodCallBaton(); | ||
| protected: | ||
| virtual void ExecuteInternal(JNIEnv* env); | ||
| JavaObject* m_javaObject; | ||
| }; | ||
| class NewInstanceBaton : public MethodCallBaton { | ||
| public: | ||
| NewInstanceBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Local<v8::Value>& callback); | ||
| virtual ~NewInstanceBaton(); | ||
| protected: | ||
| virtual void ExecuteInternal(JNIEnv* env); | ||
| jclass m_clazz; | ||
| }; | ||
| class StaticMethodCallBaton : public MethodCallBaton { | ||
| public: | ||
| StaticMethodCallBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Local<v8::Value>& callback); | ||
| virtual ~StaticMethodCallBaton(); | ||
| protected: | ||
| virtual void ExecuteInternal(JNIEnv* env); | ||
| jclass m_clazz; | ||
| }; | ||
| #endif |
| /* DO NOT EDIT THIS FILE - it is machine generated */ | ||
| #include <jni.h> | ||
| /* Header for class node_NodeDynamicProxyClass */ | ||
| #ifndef _Included_node_NodeDynamicProxyClass | ||
| #define _Included_node_NodeDynamicProxyClass | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
| /* | ||
| * Class: node_NodeDynamicProxyClass | ||
| * Method: callJs | ||
| * Signature: (JLjava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; | ||
| */ | ||
| JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs | ||
| (JNIEnv *, jobject, jlong, jobject, jobjectArray); | ||
| /* | ||
| * Class: node_NodeDynamicProxyClass | ||
| * Method: unref | ||
| * Signature: (J)V | ||
| */ | ||
| JNIEXPORT void JNICALL Java_node_NodeDynamicProxyClass_unref | ||
| (JNIEnv *, jobject, jlong); | ||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
| #endif |
| #include "java.h" | ||
| #include "javaObject.h" | ||
| extern "C" { | ||
| static void init(v8::Local<v8::Object> target, v8::Local<v8::Value>, void*) { | ||
| Java::Init(target); | ||
| JavaObject::Init(target); | ||
| } | ||
| NODE_MODULE(nodejavabridge_bindings, init); | ||
| } | ||
| #ifdef WIN32 | ||
| BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { | ||
| return TRUE; | ||
| } | ||
| #endif |
+1000
| #include "utils.h" | ||
| #include <string.h> | ||
| #include <algorithm> | ||
| #include <sstream> | ||
| #include <set> | ||
| #include "javaObject.h" | ||
| #include "java.h" | ||
| #define MODIFIER_STATIC 9 | ||
| jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj); | ||
| jobject v8ToJava_javaLong(JNIEnv* env, v8::Local<v8::Object> obj); | ||
| void javaReflectionGetMethods(JNIEnv *env, jclass clazz, std::list<jobject>* methods, bool includeStatic) { | ||
| jclass clazzclazz = env->FindClass("java/lang/Class"); | ||
| jmethodID clazz_getMethods = env->GetMethodID(clazzclazz, "getMethods", "()[Ljava/lang/reflect/Method;"); | ||
| jclass methodClazz = env->FindClass("java/lang/reflect/Method"); | ||
| jmethodID method_getModifiers = env->GetMethodID(methodClazz, "getModifiers", "()I"); | ||
| jobjectArray methodObjects = (jobjectArray)env->CallObjectMethod(clazz, clazz_getMethods); | ||
| checkJavaException(env); | ||
| jsize methodCount = env->GetArrayLength(methodObjects); | ||
| for(jsize i=0; i<methodCount; i++) { | ||
| jobject method = env->GetObjectArrayElement(methodObjects, i); | ||
| jint methodModifiers = env->CallIntMethod(method, method_getModifiers); | ||
| assertNoException(env); | ||
| if(!includeStatic && (methodModifiers & MODIFIER_STATIC) == MODIFIER_STATIC) { | ||
| continue; | ||
| } | ||
| methods->push_back(method); | ||
| } | ||
| } | ||
| void javaReflectionGetConstructors(JNIEnv *env, jclass clazz, std::list<jobject>* methods) { | ||
| jclass clazzclazz = env->FindClass("java/lang/Class"); | ||
| jmethodID clazz_getConstructors = env->GetMethodID(clazzclazz, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); | ||
| jobjectArray constructorObjects = (jobjectArray)env->CallObjectMethod(clazz, clazz_getConstructors); | ||
| checkJavaException(env); | ||
| jsize constructorCount = env->GetArrayLength(constructorObjects); | ||
| for(jsize i=0; i<constructorCount; i++) { | ||
| jobject constructor = env->GetObjectArrayElement(constructorObjects, i); | ||
| methods->push_back(constructor); | ||
| } | ||
| } | ||
| void javaReflectionGetFields(JNIEnv *env, jclass clazz, std::list<jobject>* fields) { | ||
| jclass clazzclazz = env->FindClass("java/lang/Class"); | ||
| jmethodID clazz_getFields = env->GetMethodID(clazzclazz, "getFields", "()[Ljava/lang/reflect/Field;"); | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_getModifiers = env->GetMethodID(fieldClazz, "getModifiers", "()I"); | ||
| jobjectArray fieldObjects = (jobjectArray)env->CallObjectMethod(clazz, clazz_getFields); | ||
| assertNoException(env); | ||
| jsize fieldCount = env->GetArrayLength(fieldObjects); | ||
| for(jsize i=0; i<fieldCount; i++) { | ||
| jobject field = env->GetObjectArrayElement(fieldObjects, i); | ||
| jint fieldModifiers = env->CallIntMethod(field, field_getModifiers); | ||
| checkJavaException(env); | ||
| if((fieldModifiers & MODIFIER_STATIC) == MODIFIER_STATIC) { | ||
| continue; | ||
| } | ||
| fields->push_back(field); | ||
| } | ||
| } | ||
| std::string javaToString(JNIEnv *env, jstring str) { | ||
| jclass objClazz = env->GetObjectClass(str); | ||
| jmethodID methodId = env->GetMethodID(objClazz, "getBytes", "(Ljava/lang/String;)[B"); | ||
| jstring charsetName = env->NewStringUTF("UTF-8"); | ||
| jbyteArray stringJbytes = (jbyteArray)env->CallObjectMethod(str, methodId, charsetName); | ||
| env->DeleteLocalRef(charsetName); | ||
| jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); | ||
| const jsize length = env->GetArrayLength(stringJbytes); | ||
| std::string results((const char*)pBytes, length); | ||
| env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); | ||
| env->DeleteLocalRef(stringJbytes); | ||
| return results; | ||
| } | ||
| std::string javaArrayToString(JNIEnv *env, jobjectArray arr) { | ||
| if(arr == NULL) { | ||
| return "(null)"; | ||
| } | ||
| std::ostringstream result; | ||
| result << "["; | ||
| jsize count = env->GetArrayLength(arr); | ||
| for(jsize i=0; i<count; i++) { | ||
| if(i != 0) { | ||
| result << ", "; | ||
| } | ||
| jobject obj = env->GetObjectArrayElement(arr, i); | ||
| result << javaObjectToString(env, obj); | ||
| } | ||
| result << "]"; | ||
| return result.str(); | ||
| } | ||
| std::string javaObjectToString(JNIEnv *env, jobject obj) { | ||
| if(obj == NULL) { | ||
| return "(null)"; | ||
| } | ||
| jclass objClazz = env->GetObjectClass(obj); | ||
| jmethodID methodId = env->GetMethodID(objClazz, "toString", "()Ljava/lang/String;"); | ||
| jstring result = (jstring)env->CallObjectMethod(obj, methodId); | ||
| assertNoException(env); | ||
| return javaToString(env, result); | ||
| } | ||
| std::string javaMethodCallToString(JNIEnv *env, jobject obj, jmethodID methodId, jarray args) { | ||
| char temp[100]; | ||
| std::ostringstream result; | ||
| sprintf(temp, "%p", env); | ||
| result << temp; | ||
| result << ": "; | ||
| result << javaObjectToString(env, obj); | ||
| result << ": "; | ||
| sprintf(temp, "%p", methodId); | ||
| result << temp; | ||
| result << ": ("; | ||
| jsize arraySize = env->GetArrayLength(args); | ||
| for(int i=0; i<arraySize; i++) { | ||
| if(i != 0) { | ||
| result << ", "; | ||
| } | ||
| jobject arg = env->GetObjectArrayElement((jobjectArray)args, i); | ||
| result << javaObjectToString(env, arg); | ||
| } | ||
| result << ")"; | ||
| return result.str(); | ||
| } | ||
| JNIEnv* javaGetEnv(JavaVM* jvm, jobject classLoader) { | ||
| JNIEnv *env = NULL; | ||
| int ret = jvm->GetEnv((void**)&env, JNI_BEST_VERSION); | ||
| if (ret == JNI_EDETACHED) { | ||
| JavaVMAttachArgs attachArgs; | ||
| attachArgs.version = JNI_BEST_VERSION; | ||
| attachArgs.name = NULL; | ||
| attachArgs.group = NULL; | ||
| jvm->AttachCurrentThread((void**)&env, &attachArgs); | ||
| jclass threadClazz = env->FindClass("java/lang/Thread"); | ||
| jmethodID thread_currentThread = env->GetStaticMethodID(threadClazz, "currentThread", "()Ljava/lang/Thread;"); | ||
| jmethodID thread_setContextClassLoader = env->GetMethodID(threadClazz, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V"); | ||
| jobject currentThread = env->CallStaticObjectMethod(threadClazz, thread_currentThread); | ||
| checkJavaException(env); | ||
| env->CallObjectMethod(currentThread, thread_setContextClassLoader, classLoader); | ||
| assertNoException(env); | ||
| env->DeleteLocalRef(threadClazz); | ||
| env->DeleteLocalRef(currentThread); | ||
| } | ||
| return env; | ||
| } | ||
| jobject getSystemClassLoader(JNIEnv *env) { | ||
| jclass threadClazz = env->FindClass("java/lang/Thread"); | ||
| jmethodID thread_currentThread = env->GetStaticMethodID(threadClazz, "currentThread", "()Ljava/lang/Thread;"); | ||
| jmethodID thread_getContextClassLoader = env->GetMethodID(threadClazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;"); | ||
| jobject currentThread = env->CallStaticObjectMethod(threadClazz, thread_currentThread); | ||
| checkJavaException(env); | ||
| jobject result = env->CallObjectMethod(currentThread, thread_getContextClassLoader); | ||
| checkJavaException(env); | ||
| return result; | ||
| } | ||
| jvalueType javaGetType(JNIEnv *env, jclass type) { | ||
| jclass clazzClazz = env->FindClass("java/lang/Class"); | ||
| jmethodID class_isArray = env->GetMethodID(clazzClazz, "isArray", "()Z"); | ||
| jboolean isArray = env->CallBooleanMethod(type, class_isArray); | ||
| assertNoException(env); | ||
| if(isArray) { | ||
| return TYPE_ARRAY; | ||
| } else { | ||
| // TODO: has to be a better way | ||
| std::string str = javaObjectToString(env, type); | ||
| const char *typeStr = str.c_str(); | ||
| //printf("javaGetType: %s\n", typeStr); | ||
| if(strcmp(typeStr, "void") == 0) { | ||
| return TYPE_VOID; | ||
| } else if(strcmp(typeStr, "char") == 0 || strcmp(typeStr, "class java.lang.Character") == 0) { | ||
| return TYPE_CHAR; | ||
| } else if(strcmp(typeStr, "int") == 0 || strcmp(typeStr, "class java.lang.Integer") == 0) { | ||
| return TYPE_INT; | ||
| } else if(strcmp(typeStr, "double") == 0 || strcmp(typeStr, "class java.lang.Double") == 0) { | ||
| return TYPE_DOUBLE; | ||
| } else if(strcmp(typeStr, "float") == 0 || strcmp(typeStr, "class java.lang.Float") == 0) { | ||
| return TYPE_FLOAT; | ||
| } else if(strcmp(typeStr, "long") == 0 || strcmp(typeStr, "class java.lang.Long") == 0) { | ||
| return TYPE_LONG; | ||
| } else if(strcmp(typeStr, "boolean") == 0 || strcmp(typeStr, "class java.lang.Boolean") == 0) { | ||
| return TYPE_BOOLEAN; | ||
| } else if(strcmp(typeStr, "short") == 0 || strcmp(typeStr, "class java.lang.Short") == 0) { | ||
| return TYPE_SHORT; | ||
| } else if(strcmp(typeStr, "byte") == 0 || strcmp(typeStr, "class java.lang.Byte") == 0) { | ||
| return TYPE_BYTE; | ||
| } else if(strcmp(typeStr, "class java.lang.String") == 0) { | ||
| return TYPE_STRING; | ||
| } | ||
| return TYPE_OBJECT; | ||
| } | ||
| } | ||
| jclass javaFindClass(JNIEnv* env, const std::string& className) { | ||
| std::string searchClassName = className; | ||
| std::replace(searchClassName.begin(), searchClassName.end(), '.', '/'); | ||
| // Alternate find class trying to fix Class.forName | ||
| // jclass threadClazz = env->FindClass("java/lang/Thread"); | ||
| // jmethodID thread_getCurrentThread = env->GetStaticMethodID(threadClazz, "currentThread", "()Ljava/lang/Thread;"); | ||
| // jmethodID thread_getContextClassLoader = env->GetMethodID(threadClazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;"); | ||
| // | ||
| // jclass classLoaderClazz = env->FindClass("java/lang/ClassLoader"); | ||
| // jmethodID classLoader_loadClass = env->GetMethodID(classLoaderClazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); | ||
| // | ||
| // jobject currentThread = env->CallObjectMethod(threadClazz, thread_getCurrentThread); | ||
| // jobject classLoader = env->CallObjectMethod(currentThread, thread_getContextClassLoader); | ||
| // jstring searchClassNameJava = env->NewStringUTF(className.c_str()); | ||
| // jclass clazz = (jclass)env->CallObjectMethod(classLoader, classLoader_loadClass, searchClassNameJava); | ||
| jclass clazz = env->FindClass(searchClassName.c_str()); | ||
| return clazz; | ||
| } | ||
| jobject javaFindField(JNIEnv* env, jclass clazz, const std::string& fieldName) { | ||
| jobject result = NULL; | ||
| jclass clazzclazz = env->GetObjectClass(clazz); | ||
| jclass fieldClazz = env->FindClass("java/lang/reflect/Field"); | ||
| jmethodID field_getName = env->GetMethodID(fieldClazz, "getName", "()Ljava/lang/String;"); | ||
| jmethodID class_getFields = env->GetMethodID(clazzclazz, "getFields", "()[Ljava/lang/reflect/Field;"); | ||
| jobjectArray fieldObjects = (jobjectArray)env->CallObjectMethod(clazz, class_getFields); | ||
| checkJavaException(env); | ||
| jsize fieldCount = env->GetArrayLength(fieldObjects); | ||
| for(jsize i=0; i<fieldCount; i++) { | ||
| jobject field = env->GetObjectArrayElement(fieldObjects, i); | ||
| jstring fieldNameJava = (jstring)env->CallObjectMethod(field, field_getName); | ||
| checkJavaException(env); | ||
| std::string itFieldName = javaToString(env, fieldNameJava); | ||
| if(strcmp(itFieldName.c_str(), fieldName.c_str()) == 0) { | ||
| result = field; | ||
| break; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| const std::string kObject("java/lang/Object"); | ||
| const std::string kString("java/lang/String"); | ||
| const std::string kInteger("java/lang/Integer"); | ||
| const std::string kNumber("java/lang/Number"); | ||
| const std::string kLong("java/lang/Long"); | ||
| const std::string kDouble("java/lang/Double"); | ||
| const std::string kBoolean("java/lang/Boolean"); | ||
| static std::string getArrayElementType(v8::Local<v8::Array> array, uint32_t arraySize) { | ||
| std::set<std::string> types; | ||
| if (arraySize == 0) { | ||
| return kObject; | ||
| } | ||
| for(uint32_t i=0; i<arraySize; i++) { | ||
| v8::Local<v8::Value> 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. | ||
| } | ||
| else if(arg->IsString()) { | ||
| types.insert(kString); | ||
| } | ||
| else if(arg->IsInt32() || arg->IsUint32()) { | ||
| types.insert(kInteger); | ||
| } | ||
| else if(arg->IsNumber()) { | ||
| types.insert(kDouble); | ||
| } | ||
| else if(arg->IsBoolean()) { | ||
| types.insert(kBoolean); | ||
| } | ||
| else if(arg->IsObject()) { | ||
| v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(arg); | ||
| v8::Local<v8::Value> isJavaLong = GetHiddenValue(obj, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_LONG).ToLocalChecked()); | ||
| if(!isJavaLong.IsEmpty() && isJavaLong->IsBoolean()) { | ||
| types.insert(kLong); | ||
| } | ||
| else { | ||
| return kObject; // We can exit as soon as we know java/lang/Object is required. | ||
| } | ||
| } | ||
| } | ||
| if(types.size() == 1) { | ||
| return *(types.begin()); | ||
| } | ||
| assert(types.size() >= 1); | ||
| assert(types.find(kObject)==types.end()); | ||
| // We have an array with two or more types. All types can be converted to Object, but there is one other | ||
| // case we support, which is when all the types are numeric types. | ||
| // We currently have only two non-numeric types. If neither is present in the set, the rest must be numeric. | ||
| if (types.find(kString)==types.end() && types.find(kBoolean)==types.end()) { | ||
| return kNumber; | ||
| } | ||
| return kObject; | ||
| } | ||
| jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) { | ||
| if(arg.IsEmpty() || arg->IsNull() || arg->IsUndefined()) { | ||
| return NULL; | ||
| } | ||
| if(arg->IsArray()) { | ||
| v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(arg); | ||
| uint32_t arraySize = array->Length(); | ||
| std::string arrayType = getArrayElementType(array, arraySize); | ||
| jclass objectClazz = env->FindClass(arrayType.c_str()); | ||
| jobjectArray result = env->NewObjectArray(arraySize, objectClazz, NULL); | ||
| for(uint32_t i=0; i<arraySize; i++) { | ||
| jobject val = v8ToJava(env, array->Get(Nan::GetCurrentContext(), i).ToLocalChecked()); | ||
| env->SetObjectArrayElement(result, i, val); | ||
| } | ||
| return result; | ||
| } | ||
| if(arg->IsString()) { | ||
| #if NODE_MAJOR_VERSION > 7 | ||
| v8::String::Value val(v8::Isolate::GetCurrent(), arg->ToString(Nan::GetCurrentContext()).ToLocalChecked()); | ||
| #else | ||
| v8::String::Value val(arg->ToString(Nan::GetCurrentContext()).ToLocalChecked()); | ||
| #endif | ||
| return env->NewString(*val, val.length()); | ||
| } | ||
| if(arg->IsInt32() || arg->IsUint32()) { | ||
| jint val = Nan::To<int32_t>(arg).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Integer"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(I)V"); | ||
| return env->NewObject(clazz, constructor, val); | ||
| } | ||
| if(arg->IsNumber()) { | ||
| jdouble val = Nan::To<double>(arg).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Double"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(D)V"); | ||
| return env->NewObject(clazz, constructor, val); | ||
| } | ||
| if(arg->IsBoolean()) { | ||
| jboolean val = Nan::To<bool>(arg).FromJust(); | ||
| jclass clazz = env->FindClass("java/lang/Boolean"); | ||
| jmethodID constructor = env->GetMethodID(clazz, "<init>", "(Z)V"); | ||
| return env->NewObject(clazz, constructor, val); | ||
| } | ||
| if(arg->IsObject()) { | ||
| v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(arg); | ||
| v8::Local<v8::Value> isJavaObject = GetHiddenValue(obj, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked()); | ||
| if(!isJavaObject.IsEmpty() && isJavaObject->IsBoolean()) { | ||
| return v8ToJava_javaObject(env, obj); | ||
| } | ||
| v8::Local<v8::Value> isJavaLong = GetHiddenValue(obj, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_LONG).ToLocalChecked()); | ||
| if(!isJavaLong.IsEmpty() && isJavaLong->IsBoolean()) { | ||
| return v8ToJava_javaLong(env, obj); | ||
| } | ||
| } | ||
| // TODO: handle other arg types. Don't print here, see instanceof-test#non-java object | ||
| // v8::String::AsciiValue typeStr(arg); | ||
| // printf("v8ToJava: Unhandled type: %s\n", *typeStr); | ||
| return NULL; | ||
| } | ||
| jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj) { | ||
| JavaObject* javaObject = Nan::ObjectWrap::Unwrap<JavaObject>(obj); | ||
| return javaObject->getObject(); | ||
| } | ||
| void checkJavaException(JNIEnv* env) { | ||
| if(env->ExceptionCheck()) { | ||
| jthrowable ex = env->ExceptionOccurred(); | ||
| env->ExceptionClear(); | ||
| std::string exString = javaExceptionToString(env, ex); | ||
| printf("%s\n", exString.c_str()); | ||
| assert(false); | ||
| } | ||
| } | ||
| jobject v8ToJava_javaLong(JNIEnv* env, v8::Local<v8::Object> obj) { | ||
| jobject longValue = v8ToJava(env, obj->Get(Nan::GetCurrentContext(), Nan::New<v8::String>("longValue").ToLocalChecked()).ToLocalChecked()); | ||
| jclass longClazz = env->FindClass("java/lang/Long"); | ||
| jmethodID long_constructor = env->GetMethodID(longClazz, "<init>", "(Ljava/lang/String;)V"); | ||
| jobject jobj = env->NewObject(longClazz, long_constructor, longValue); | ||
| return jobj; | ||
| } | ||
| jobjectArray v8ToJava(JNIEnv* env, Nan::NAN_METHOD_ARGS_TYPE args, int start, int end) { | ||
| jclass clazz = env->FindClass("java/lang/Object"); | ||
| jobjectArray results = env->NewObjectArray(end-start, clazz, NULL); | ||
| for(int i=start; i<end; i++) { | ||
| jobject val = v8ToJava(env, args[i]); | ||
| env->SetObjectArrayElement(results, i - start, val); | ||
| } | ||
| return results; | ||
| } | ||
| std::string javaExceptionToString(JNIEnv* env, jthrowable ex) { | ||
| jclass stringWriterClazz = env->FindClass("java/io/StringWriter"); | ||
| jmethodID stringWriter_constructor = env->GetMethodID(stringWriterClazz, "<init>", "()V"); | ||
| jmethodID stringWriter_toString = env->GetMethodID(stringWriterClazz, "toString", "()Ljava/lang/String;"); | ||
| jobject stringWriter = env->NewObject(stringWriterClazz, stringWriter_constructor); | ||
| jclass printWriterClazz = env->FindClass("java/io/PrintWriter"); | ||
| jmethodID printWriter_constructor = env->GetMethodID(printWriterClazz, "<init>", "(Ljava/io/Writer;)V"); | ||
| jobject printWriter = env->NewObject(printWriterClazz, printWriter_constructor, stringWriter); | ||
| jclass throwableClazz = env->FindClass("java/lang/Throwable"); | ||
| jmethodID throwable_printStackTrace = env->GetMethodID(throwableClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V"); | ||
| env->CallObjectMethod(ex, throwable_printStackTrace, printWriter); | ||
| checkJavaException(env); | ||
| jstring strObj = (jstring)env->CallObjectMethod(stringWriter, stringWriter_toString); | ||
| checkJavaException(env); | ||
| return javaToString(env, strObj); | ||
| } | ||
| v8::Local<v8::Value> javaExceptionToV8(Java* java, JNIEnv* env, jthrowable ex, const std::string& alternateMessage) { | ||
| std::ostringstream msg; | ||
| msg << alternateMessage; | ||
| if(ex) { | ||
| msg << "\n" << javaExceptionToString(env, ex); | ||
| v8::Local<v8::Value> v8ex = v8::Exception::Error(Nan::New<v8::String>(msg.str().c_str()).ToLocalChecked()); | ||
| ((v8::Object*)*v8ex)->Set(Nan::GetCurrentContext(), Nan::New<v8::String>("cause").ToLocalChecked(), javaToV8(java, env, ex)); | ||
| return v8ex; | ||
| } | ||
| return v8::Exception::Error(Nan::New<v8::String>(msg.str().c_str()).ToLocalChecked()); | ||
| } | ||
| v8::Local<v8::Value> javaExceptionToV8(Java* java, JNIEnv* env, const std::string& alternateMessage) { | ||
| jthrowable ex = env->ExceptionOccurred(); | ||
| env->ExceptionClear(); | ||
| return javaExceptionToV8(java, env, ex, alternateMessage); | ||
| } | ||
| jvalueType javaGetArrayComponentType(JNIEnv *env, jobjectArray array) { | ||
| jclass objectClazz = env->FindClass("java/lang/Object"); | ||
| jclass clazzclazz = env->FindClass("java/lang/Class"); | ||
| jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;"); | ||
| jobject arrayClass = env->CallObjectMethod(array, object_getClass); | ||
| assertNoException(env); | ||
| jmethodID class_getComponentType = env->GetMethodID(clazzclazz, "getComponentType", "()Ljava/lang/Class;"); | ||
| jobject arrayComponentTypeClass = env->CallObjectMethod(arrayClass, class_getComponentType); | ||
| checkJavaException(env); | ||
| jvalueType arrayComponentType = javaGetType(env, (jclass)arrayComponentTypeClass); | ||
| return arrayComponentType; | ||
| } | ||
| #if NODE_VERSION_AT_LEAST(13, 0, 0) | ||
| v8::Local<v8::ArrayBuffer> newArrayBuffer(void* elems, size_t length) { | ||
| v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(v8::Isolate::GetCurrent(), length); | ||
| memcpy(ab->GetBackingStore()->Data(), elems, length); | ||
| return ab; | ||
| } | ||
| #elif NODE_VERSION_AT_LEAST(4, 0, 0) | ||
| v8::Local<v8::ArrayBuffer> newArrayBuffer(void* elems, size_t length) { | ||
| v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(v8::Isolate::GetCurrent(), length); | ||
| memcpy(ab->GetContents().Data(), elems, length); | ||
| return ab; | ||
| } | ||
| #endif | ||
| v8::Local<v8::String> javaCharToV8String(jchar c) { | ||
| #if ((NODE_MAJOR_VERSION == 0) && (NODE_MINOR_VERSION <= 10)) | ||
| return v8::String::New(&c, 1); | ||
| #elif ((NODE_MAJOR_VERSION == 0) && (NODE_MINOR_VERSION <= 12)) | ||
| return v8::String::NewFromTwoByte(v8::Isolate::GetCurrent(), &c, v8::String::kNormalString, 1); | ||
| #else | ||
| return v8::String::NewFromTwoByte(v8::Isolate::GetCurrent(), &c, v8::NewStringType::kNormal, 1).ToLocalChecked(); | ||
| #endif | ||
| } | ||
| v8::Local<v8::Value> javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArray) { | ||
| if(objArray == NULL) { | ||
| return Nan::Null(); | ||
| } | ||
| jvalueType arrayComponentType = javaGetArrayComponentType(env, objArray); | ||
| //printf("javaArrayToV8: %d %s\n", arrayComponentType, javaObjectToString(env, objArray).c_str()); | ||
| jsize arraySize = env->GetArrayLength(objArray); | ||
| //printf("array size: %d\n", arraySize); | ||
| v8::Local<v8::Array> result = Nan::New<v8::Array>(arraySize); | ||
| // http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html | ||
| switch(arrayComponentType) { | ||
| case TYPE_CHAR: | ||
| { | ||
| jchar* elems = env->GetCharArrayElements((jcharArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize * 2; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseCharArrayElements((jcharArray)objArray, elems, 0); | ||
| return v8::Uint16Array::New(ab, 0, arraySize); | ||
| #else | ||
| jchar str; | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| str = elems[i]; | ||
| result->Set(i, javaCharToV8String(str)); | ||
| } | ||
| env->ReleaseCharArrayElements((jcharArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_INT: | ||
| { | ||
| jint* elems = env->GetIntArrayElements((jintArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize * 4; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseIntArrayElements((jintArray)objArray, elems, 0); | ||
| return v8::Int32Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Integer>(elems[i])); | ||
| } | ||
| env->ReleaseIntArrayElements((jintArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_BYTE: | ||
| { | ||
| jbyte* elems = env->GetByteArrayElements((jbyteArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseByteArrayElements((jbyteArray)objArray, elems, 0); | ||
| return v8::Int8Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Number>(elems[i])); | ||
| } | ||
| env->ReleaseByteArrayElements((jbyteArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_BOOLEAN: | ||
| { | ||
| jboolean* elems = env->GetBooleanArrayElements((jbooleanArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseBooleanArrayElements((jbooleanArray)objArray, elems, 0); | ||
| return v8::Uint8Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Boolean>(elems[i])); | ||
| } | ||
| env->ReleaseBooleanArrayElements((jbooleanArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_SHORT: | ||
| { | ||
| jshort* elems = env->GetShortArrayElements((jshortArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize * 2; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseShortArrayElements((jshortArray)objArray, elems, 0); | ||
| return v8::Int16Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Number>(elems[i])); | ||
| } | ||
| env->ReleaseShortArrayElements((jshortArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_DOUBLE: | ||
| { | ||
| jdouble* elems = env->GetDoubleArrayElements((jdoubleArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize * 8; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseDoubleArrayElements((jdoubleArray)objArray, elems, 0); | ||
| return v8::Float64Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Number>(elems[i])); | ||
| } | ||
| env->ReleaseDoubleArrayElements((jdoubleArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_FLOAT: | ||
| { | ||
| jfloat* elems = env->GetFloatArrayElements((jfloatArray)objArray, 0); | ||
| #if (NODE_VERSION_AT_LEAST(4, 0, 0)) | ||
| size_t byteLength = arraySize * 4; | ||
| v8::Local<v8::ArrayBuffer> ab = newArrayBuffer(elems, byteLength); | ||
| env->ReleaseFloatArrayElements((jfloatArray)objArray, elems, 0); | ||
| return v8::Float32Array::New(ab, 0, arraySize); | ||
| #else | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| result->Set(i, Nan::New<v8::Number>(elems[i])); | ||
| } | ||
| env->ReleaseFloatArrayElements((jfloatArray)objArray, elems, 0); | ||
| #endif | ||
| } | ||
| break; | ||
| case TYPE_LONG: | ||
| { | ||
| jlong* elems = env->GetLongArrayElements((jlongArray)objArray, 0); | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| jobject obj = longToJavaLongObj(env, elems[i]); | ||
| result->Set(Nan::GetCurrentContext(), i, JavaObject::New(java, obj)); | ||
| } | ||
| env->ReleaseLongArrayElements((jlongArray)objArray, elems, 0); | ||
| } | ||
| break; | ||
| default: | ||
| for(jsize i=0; i<arraySize; i++) { | ||
| jobject obj = env->GetObjectArrayElement(objArray, i); | ||
| v8::Local<v8::Value> item = javaToV8(java, env, obj); | ||
| result->Set(Nan::GetCurrentContext(), i, item); | ||
| } | ||
| break; | ||
| } | ||
| return result; | ||
| } | ||
| v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj) { | ||
| return javaToV8(java, env, obj, NULL); | ||
| } | ||
| v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxyData* dynamicProxyData) { | ||
| if(obj == NULL) { | ||
| return Nan::Null(); | ||
| } | ||
| jclass objClazz = env->GetObjectClass(obj); | ||
| jvalueType resultType = javaGetType(env, objClazz); | ||
| //printf("javaToV8: %d %s\n", resultType, javaObjectToString(env, obj).c_str()); | ||
| switch(resultType) { | ||
| case TYPE_ARRAY: | ||
| { | ||
| v8::Local<v8::Value> result = javaArrayToV8(java, env, (jobjectArray)obj); | ||
| return result; | ||
| } | ||
| case TYPE_VOID: | ||
| return Nan::Undefined(); | ||
| case TYPE_CHAR: | ||
| { | ||
| jclass charClazz = env->FindClass("java/lang/Character"); | ||
| jmethodID char_charValue = env->GetMethodID(charClazz, "charValue", "()C"); | ||
| jchar c = env->CallCharMethod(obj, char_charValue); | ||
| checkJavaException(env); | ||
| return javaCharToV8String(c); | ||
| } | ||
| case TYPE_BOOLEAN: | ||
| { | ||
| jclass booleanClazz = env->FindClass("java/lang/Boolean"); | ||
| jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z"); | ||
| bool result = env->CallBooleanMethod(obj, boolean_booleanValue); | ||
| assertNoException(env); | ||
| return Nan::New<v8::Boolean>(result); | ||
| } | ||
| case TYPE_BYTE: | ||
| { | ||
| jclass byteClazz = env->FindClass("java/lang/Byte"); | ||
| jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B"); | ||
| jbyte result = env->CallByteMethod(obj, byte_byteValue); | ||
| checkJavaException(env); | ||
| return Nan::New<v8::Number>(result); | ||
| } | ||
| case TYPE_LONG: | ||
| { | ||
| jclass longClazz = env->FindClass("java/lang/Long"); | ||
| jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J"); | ||
| jlong result = env->CallLongMethod(obj, long_longValue); | ||
| checkJavaException(env); | ||
| std::string strValue = javaObjectToString(env, obj); | ||
| v8::Local<v8::Value> v8Result = Nan::New<v8::NumberObject>((double)result); | ||
| v8::NumberObject* v8ResultNumberObject = v8::NumberObject::Cast(*v8Result); | ||
| v8ResultNumberObject->Set(Nan::GetCurrentContext(), Nan::New<v8::String>("longValue").ToLocalChecked(), Nan::New<v8::String>(strValue.c_str()).ToLocalChecked()); | ||
| SetHiddenValue(v8ResultNumberObject, Nan::New<v8::String>(V8_HIDDEN_MARKER_JAVA_LONG).ToLocalChecked(), Nan::New<v8::Boolean>(true)); | ||
| return v8Result; | ||
| } | ||
| case TYPE_INT: | ||
| { | ||
| jclass integerClazz = env->FindClass("java/lang/Integer"); | ||
| jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I"); | ||
| jint result = env->CallIntMethod(obj, integer_intValue); | ||
| checkJavaException(env); | ||
| return Nan::New<v8::Integer>(result); | ||
| } | ||
| case TYPE_SHORT: | ||
| { | ||
| jclass shortClazz = env->FindClass("java/lang/Short"); | ||
| jmethodID short_shortValue = env->GetMethodID(shortClazz, "shortValue", "()S"); | ||
| jshort result = env->CallShortMethod(obj, short_shortValue); | ||
| assertNoException(env); | ||
| return Nan::New<v8::Integer>(result); | ||
| } | ||
| case TYPE_DOUBLE: | ||
| { | ||
| jclass doubleClazz = env->FindClass("java/lang/Double"); | ||
| jmethodID double_doubleValue = env->GetMethodID(doubleClazz, "doubleValue", "()D"); | ||
| jdouble result = env->CallDoubleMethod(obj, double_doubleValue); | ||
| checkJavaException(env); | ||
| return Nan::New<v8::Number>(result); | ||
| } | ||
| case TYPE_FLOAT: | ||
| { | ||
| jclass floatClazz = env->FindClass("java/lang/Float"); | ||
| jmethodID float_floatValue = env->GetMethodID(floatClazz, "floatValue", "()F"); | ||
| jfloat result = env->CallFloatMethod(obj, float_floatValue); | ||
| assertNoException(env); | ||
| return Nan::New<v8::Number>(result); | ||
| } | ||
| case TYPE_STRING: | ||
| { | ||
| std::string str = javaObjectToString(env, obj); | ||
| return Nan::New<v8::String>(str.c_str(), str.length()).ToLocalChecked(); | ||
| } | ||
| case TYPE_OBJECT: | ||
| if (dynamicProxyData != NULL) { | ||
| return JavaProxyObject::New(java, obj, dynamicProxyData); | ||
| } | ||
| return JavaObject::New(java, obj); | ||
| default: | ||
| printf("javaToV8: unhandled type: 0x%03x\n", resultType); | ||
| return JavaObject::New(java, obj); | ||
| } | ||
| return Nan::Undefined(); | ||
| } | ||
| jobjectArray javaObjectArrayToClasses(JNIEnv *env, jobjectArray objs) { | ||
| jclass clazzClazz = env->FindClass("java/lang/Class"); | ||
| jsize objsLength = env->GetArrayLength(objs); | ||
| jobjectArray results = env->NewObjectArray(objsLength, clazzClazz, NULL); | ||
| for(jsize i=0; i<objsLength; i++) { | ||
| jobject elem = env->GetObjectArrayElement(objs, i); | ||
| if(elem == NULL) { | ||
| env->SetObjectArrayElement(results, i, NULL); | ||
| } else { | ||
| jclass objClazz = env->GetObjectClass(elem); | ||
| env->SetObjectArrayElement(results, i, objClazz); | ||
| } | ||
| } | ||
| return results; | ||
| } | ||
| jobject javaFindMethod(JNIEnv *env, jclass clazz, const std::string& methodName, jobjectArray methodArgs) { | ||
| std::string::size_type parenLoc = methodName.find("("); | ||
| if(parenLoc != std::string::npos) { | ||
| jobject method = NULL; | ||
| std::string methodSig = methodName.substr(parenLoc); | ||
| std::string methodRealName = methodName.substr(0, parenLoc); | ||
| jmethodID methodID = env->GetStaticMethodID(clazz, methodRealName.c_str(), methodSig.c_str()); | ||
| env->ExceptionClear(); // If GetStaticMethodID can't find the method it throws an exception and we need to just return NULL | ||
| if(methodID != 0) { | ||
| method = env->ToReflectedMethod(clazz, methodID, true); | ||
| } else { | ||
| methodID = env->GetMethodID(clazz, methodRealName.c_str(), methodSig.c_str()); | ||
| env->ExceptionClear(); // If GetMethodID can't find the method it throws an exception and we need to just return NULL | ||
| if(methodID != 0) { | ||
| method = env->ToReflectedMethod(clazz, methodID, true); | ||
| } | ||
| } | ||
| // cast arguments | ||
| if(method != NULL) { | ||
| javaCastArguments(env, methodArgs, method); | ||
| if(env->ExceptionCheck()) { | ||
| env->ExceptionClear(); | ||
| method = NULL; | ||
| } | ||
| } | ||
| return method; | ||
| } else { | ||
| jclass methodUtilsClazz = env->FindClass("nodejava/org/apache/commons/lang3/reflect/MethodUtils"); | ||
| jmethodID methodUtils_getMatchingAccessibleMethod = env->GetStaticMethodID(methodUtilsClazz, "getMatchingAccessibleMethod", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); | ||
| const char *methodNameCStr = methodName.c_str(); | ||
| jstring methodNameJavaStr = env->NewStringUTF(methodNameCStr); | ||
| jobjectArray methodArgClasses = javaObjectArrayToClasses(env, methodArgs); | ||
| jobject method = env->CallStaticObjectMethod(methodUtilsClazz, methodUtils_getMatchingAccessibleMethod, clazz, methodNameJavaStr, methodArgClasses); | ||
| checkJavaException(env); | ||
| return method; | ||
| } | ||
| } | ||
| void javaCastArguments(JNIEnv *env, jobjectArray methodArgs, jobject method) { | ||
| jclass castingUtilsClazz = env->FindClass("node/CastingUtils"); | ||
| jmethodID castingUtilsClazz_cast = env->GetStaticMethodID(castingUtilsClazz, "cast", "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)V"); | ||
| env->CallStaticObjectMethod(castingUtilsClazz, castingUtilsClazz_cast, method, methodArgs); | ||
| } | ||
| jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs) { | ||
| jclass constructorUtilsClazz = env->FindClass("nodejava/org/apache/commons/lang3/reflect/ConstructorUtils"); | ||
| jmethodID constructorUtils_getMatchingAccessibleConstructor = env->GetStaticMethodID(constructorUtilsClazz, "getMatchingAccessibleConstructor", "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); | ||
| jobjectArray methodArgClasses = javaObjectArrayToClasses(env, methodArgs); | ||
| jobject method = env->CallStaticObjectMethod(constructorUtilsClazz, constructorUtils_getMatchingAccessibleConstructor, clazz, methodArgClasses); | ||
| assertNoException(env); | ||
| return method; | ||
| } | ||
| jobject longToJavaLongObj(JNIEnv *env, jlong val) { | ||
| jclass longClass = env->FindClass("java/lang/Long"); | ||
| jmethodID constructor = env->GetMethodID(longClass, "<init>", "(J)V"); | ||
| jobject result = env->NewObject(longClass, constructor, val); | ||
| return result; | ||
| } | ||
| int dynamicProxyDataVerify(DynamicProxyData* data) { | ||
| if(data->markerStart == DYNAMIC_PROXY_DATA_MARKER_START && data->markerEnd == DYNAMIC_PROXY_DATA_MARKER_END) { | ||
| return 1; | ||
| } | ||
| printf("*** ERROR: Lost reference to the dynamic proxy. You must maintain a reference in javascript land using ref() and unref(). (%p) ***\n", data); | ||
| return 0; | ||
| } | ||
| std::string methodNotFoundToString(JNIEnv *env, jclass clazz, const std::string& methodNameSig, bool constructor, Nan::NAN_METHOD_ARGS_TYPE args, int argStart, int argEnd) { | ||
| std::ostringstream startOfMessage; | ||
| std::ostringstream msg; | ||
| std::string methodName = methodNameSig.substr(0, methodNameSig.find('(')); | ||
| jclass classClazz = env->FindClass("java/lang/Class"); | ||
| jmethodID class_getName = env->GetMethodID(classClazz, "getName", "()Ljava/lang/String;"); | ||
| if (methodName != methodNameSig) { | ||
| startOfMessage << "Could not find method for signature \"" << methodNameSig.c_str() << "\" and arguments \"("; | ||
| } else { | ||
| startOfMessage << "Could not find method \"" << methodName.c_str() << "("; | ||
| } | ||
| for(int i=argStart; i<argEnd; i++) { | ||
| jobject val = v8ToJava(env, args[i]); | ||
| if(i != argStart) { | ||
| startOfMessage << ", "; | ||
| } | ||
| if(val == NULL) { | ||
| startOfMessage << "(null)"; | ||
| } else { | ||
| jclass argClass = env->GetObjectClass(val); | ||
| jstring argClassNameJava = (jstring)env->CallObjectMethod(argClass, class_getName); | ||
| checkJavaException(env); | ||
| std::string argClassName = javaToString(env, argClassNameJava); | ||
| startOfMessage << argClassName; | ||
| } | ||
| } | ||
| startOfMessage << ")\" on class \""<< javaObjectToString(env, clazz).c_str() << "\"."; | ||
| msg << startOfMessage.str() << " Possible matches:\n"; | ||
| jclass memberClazz = env->FindClass("java/lang/reflect/Member"); | ||
| jmethodID member_getName = env->GetMethodID(memberClazz, "getName", "()Ljava/lang/String;"); | ||
| std::list<jobject> methods; | ||
| if(constructor) { | ||
| javaReflectionGetConstructors(env, clazz, &methods); | ||
| } else { | ||
| javaReflectionGetMethods(env, clazz, &methods, true); | ||
| } | ||
| int count = 0; | ||
| for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); ++it) { | ||
| jstring methodNameTestJava = (jstring)env->CallObjectMethod(*it, member_getName); | ||
| assertNoException(env); | ||
| std::string methodNameTest = javaToString(env, methodNameTestJava); | ||
| if(methodNameTest == methodName) { | ||
| msg << " " << javaObjectToString(env, *it).c_str() << "\n"; | ||
| count++; | ||
| } | ||
| } | ||
| if(count == 0) { | ||
| std::ostringstream noMethodsMsg; | ||
| noMethodsMsg << startOfMessage.str() << " No methods with that name."; | ||
| return noMethodsMsg.str(); | ||
| } | ||
| return msg.str(); | ||
| } | ||
| void unref(DynamicProxyData* dynamicProxyData) { | ||
| if(!dynamicProxyDataVerify(dynamicProxyData)) { | ||
| return; | ||
| } | ||
| dynamicProxyData->jsObject.Reset(); | ||
| dynamicProxyData->functions.Reset(); | ||
| dynamicProxyData->markerStart = 0; | ||
| dynamicProxyData->markerEnd = 0; | ||
| delete dynamicProxyData; | ||
| } | ||
| jarray javaGetArgsForMethod(JNIEnv *env, jobject method, jarray args) { | ||
| jclass varArgsClazz = env->FindClass("node/VarArgs"); | ||
| jmethodID method_getVarArgs = env->GetStaticMethodID(varArgsClazz, "getVarArgs", "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)[Ljava/lang/Object;"); | ||
| jarray result = (jarray)env->CallStaticObjectMethod(varArgsClazz, method_getVarArgs, method, args); | ||
| checkJavaException(env); | ||
| return result; | ||
| } | ||
| jarray javaGetArgsForConstructor(JNIEnv *env, jobject method, jarray args) { | ||
| jclass varArgsClazz = env->FindClass("node/VarArgs"); | ||
| jmethodID method_getVarArgs = env->GetStaticMethodID(varArgsClazz, "getVarArgs", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)[Ljava/lang/Object;"); | ||
| jarray result = (jarray)env->CallStaticObjectMethod(varArgsClazz, method_getVarArgs, method, args); | ||
| checkJavaException(env); | ||
| return result; | ||
| } | ||
| #if (NODE_MODULE_VERSION > 48) | ||
| // The two methods below were copied from | ||
| // https://github.com/electron/electron?branch=master&filepath=atom/common/api/atom_api_v8_util.cc | ||
| // Copyright (c) 2013 GitHub, Inc. | ||
| // Use of this source code is governed by the MIT license. | ||
| v8::Local<v8::Value> GetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key) { | ||
| v8::Local<v8::Context> context = v8::Isolate::GetCurrent()->GetCurrentContext(); | ||
| v8::Local<v8::Private> privateKey = v8::Private::ForApi(v8::Isolate::GetCurrent(), key); | ||
| v8::Local<v8::Value> value; | ||
| v8::Maybe<bool> result = object->HasPrivate(context, privateKey); | ||
| if (!(result.IsJust() && result.FromJust())) | ||
| return v8::Local<v8::Value>(); | ||
| if (object->GetPrivate(context, privateKey).ToLocal(&value)) | ||
| return value; | ||
| return v8::Local<v8::Value>(); | ||
| } | ||
| void SetHiddenValue(v8::NumberObject* object, v8::Local<v8::String> key, v8::Local<v8::Value> value) { | ||
| if (value.IsEmpty()) | ||
| return; | ||
| v8::Local<v8::Context> context = v8::Isolate::GetCurrent()->GetCurrentContext(); | ||
| v8::Local<v8::Private> privateKey = v8::Private::ForApi(v8::Isolate::GetCurrent(), key); | ||
| object->SetPrivate(context, privateKey, value); | ||
| } | ||
| void SetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key, v8::Local<v8::Value> value) { | ||
| if (value.IsEmpty()) | ||
| return; | ||
| v8::Local<v8::Context> context = v8::Isolate::GetCurrent()->GetCurrentContext(); | ||
| v8::Local<v8::Private> privateKey = v8::Private::ForApi(v8::Isolate::GetCurrent(), key); | ||
| object->SetPrivate(context, privateKey, value); | ||
| } | ||
| #else | ||
| v8::Local<v8::Value> GetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key) { | ||
| return object->GetHiddenValue(key); | ||
| } | ||
| void SetHiddenValue(v8::NumberObject* object, v8::Local<v8::String> key, v8::Local<v8::Value> value) { | ||
| object->SetHiddenValue(key, value); | ||
| } | ||
| void SetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key, v8::Local<v8::Value> value) { | ||
| object->SetHiddenValue(key, value); | ||
| } | ||
| #endif |
+166
| #ifndef _utils_h_ | ||
| #define _utils_h_ | ||
| #define BUILDING_NODE_EXTENSION 1 | ||
| #include <v8.h> | ||
| #include <jni.h> | ||
| #include <list> | ||
| #include <vector> | ||
| #include <string> | ||
| #include <uv.h> | ||
| #include <nan.h> | ||
| class Java; | ||
| #define V8_HIDDEN_MARKER_JAVA_LONG "__isJavaLong" | ||
| #define V8_HIDDEN_MARKER_JAVA_OBJECT "__isJavaObject" | ||
| typedef enum _jvalueType { | ||
| TYPE_VOID = 1, | ||
| TYPE_INT = 2, | ||
| TYPE_LONG = 3, | ||
| TYPE_OBJECT = 4, | ||
| TYPE_STRING = 5, | ||
| TYPE_BOOLEAN = 6, | ||
| TYPE_SHORT = 7, | ||
| TYPE_BYTE = 8, | ||
| TYPE_DOUBLE = 9, | ||
| TYPE_FLOAT = 10, | ||
| TYPE_ARRAY = 11, | ||
| TYPE_CHAR = 12 | ||
| } jvalueType; | ||
| struct DynamicProxyData { | ||
| unsigned int markerStart; | ||
| Java* java; | ||
| std::string interfaceName; | ||
| Nan::Persistent<v8::Object> functions; | ||
| Nan::Persistent<v8::Value> jsObject; | ||
| unsigned int markerEnd; | ||
| }; | ||
| struct DynamicProxyJsCallData { | ||
| DynamicProxyData *dynamicProxyData; | ||
| std::string methodName; | ||
| jobjectArray args; | ||
| jobject result; | ||
| std::string throwableClass; | ||
| std::string throwableMessage; | ||
| int done; | ||
| }; | ||
| #define DYNAMIC_PROXY_DATA_MARKER_START 0x12345678 | ||
| #define DYNAMIC_PROXY_DATA_MARKER_END 0x87654321 | ||
| int dynamicProxyDataVerify(DynamicProxyData* data); | ||
| void javaReflectionGetMethods(JNIEnv *env, jclass clazz, std::list<jobject>* methods, bool includeStatic); | ||
| void javaReflectionGetConstructors(JNIEnv *env, jclass clazz, std::list<jobject>* methods); | ||
| void javaReflectionGetFields(JNIEnv *env, jclass clazz, std::list<jobject>* fields); | ||
| std::string javaToString(JNIEnv *env, jstring str); | ||
| std::string javaObjectToString(JNIEnv *env, jobject obj); | ||
| std::string javaArrayToString(JNIEnv *env, jobjectArray arr); | ||
| std::string javaMethodCallToString(JNIEnv *env, jobject obj, jmethodID methodId, jarray args); | ||
| JNIEnv* javaGetEnv(JavaVM* jvm, jobject classLoader); | ||
| jobject getSystemClassLoader(JNIEnv *env); | ||
| jvalueType javaGetArrayComponentType(JNIEnv *env, jobjectArray array); | ||
| jvalueType javaGetType(JNIEnv *env, jclass type); | ||
| jobjectArray v8ToJava(JNIEnv* env, Nan::NAN_METHOD_ARGS_TYPE args, int start, int end); | ||
| jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg); | ||
| v8::Local<v8::Value> javaExceptionToV8(Java* java, JNIEnv* env, const std::string& alternateMessage); | ||
| v8::Local<v8::Value> javaExceptionToV8(Java* java, JNIEnv* env, jthrowable ex, const std::string& alternateMessage); | ||
| std::string javaExceptionToString(JNIEnv* env, jthrowable ex); | ||
| void checkJavaException(JNIEnv* env); | ||
| v8::Local<v8::Value> javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArray); | ||
| v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj); | ||
| v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxyData* dynamicProxyData); | ||
| jobjectArray javaObjectArrayToClasses(JNIEnv *env, jobjectArray objs); | ||
| jobject longToJavaLongObj(JNIEnv *env, jlong l); | ||
| jarray javaGetArgsForMethod(JNIEnv *env, jobject method, jarray args); | ||
| jarray javaGetArgsForConstructor(JNIEnv *env, jobject method, jarray args); | ||
| jclass javaFindClass(JNIEnv* env, const std::string& className); | ||
| jobject javaFindField(JNIEnv* env, jclass clazz, const std::string& fieldName); | ||
| jobject javaFindMethod(JNIEnv *env, jclass clazz, const std::string& methodName, jobjectArray methodArgs); | ||
| jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs); | ||
| void javaCastArguments(JNIEnv *env, jobjectArray methodArgs, jobject method); | ||
| // TODO remove these functions after node nan gets updated | ||
| v8::Local<v8::Value> GetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key); | ||
| void SetHiddenValue(v8::Local<v8::Object> object, v8::Local<v8::String> key, v8::Local<v8::Value> value); | ||
| void SetHiddenValue(v8::NumberObject*, v8::Local<v8::String> key, v8::Local<v8::Value> value); | ||
| #define assertNoException(env) \ | ||
| if (env->ExceptionCheck()) { \ | ||
| env->ExceptionDescribe(); \ | ||
| assert(false); \ | ||
| } | ||
| std::string methodNotFoundToString(JNIEnv *env, jclass clazz, const std::string& methodNameSig, bool constructor, Nan::NAN_METHOD_ARGS_TYPE args, int argStart, int argEnd); | ||
| void unref(DynamicProxyData* dynamicProxyData); | ||
| #define UNUSED_VARIABLE(var) var = var; | ||
| #define ARGS_FRONT_OBJECT(ARGNAME) \ | ||
| if(info.Length() < argsStart+1 || !info[argsStart]->IsObject()) { \ | ||
| std::ostringstream errStr; \ | ||
| errStr << "Argument " << (argsStart+1) << " must be an object"; \ | ||
| Nan::ThrowError(Nan::TypeError(errStr.str().c_str())); \ | ||
| return; \ | ||
| } \ | ||
| v8::Local<v8::Object> ARGNAME = v8::Local<v8::Object>::Cast(info[argsStart]); \ | ||
| argsStart++; | ||
| #define ARGS_FRONT_STRING(ARGNAME) \ | ||
| if(info.Length() < argsStart+1 || !info[argsStart]->IsString()) { \ | ||
| std::ostringstream errStr; \ | ||
| errStr << "Argument " << (argsStart+1) << " must be a string"; \ | ||
| Nan::ThrowError(Nan::TypeError(errStr.str().c_str())); \ | ||
| return; \ | ||
| } \ | ||
| v8::Local<v8::String> _##ARGNAME##_obj = v8::Local<v8::String>::Cast(info[argsStart]); \ | ||
| Nan::Utf8String _##ARGNAME##_val(_##ARGNAME##_obj); \ | ||
| std::string ARGNAME = *_##ARGNAME##_val; \ | ||
| argsStart++; | ||
| #define ARGS_FRONT_CLASSNAME() ARGS_FRONT_STRING(className) | ||
| #define ARGS_BACK_CALLBACK() \ | ||
| bool callbackProvided; \ | ||
| v8::Local<v8::Value> callback; \ | ||
| if(info[info.Length()-1]->IsFunction()) { \ | ||
| callback = info[argsEnd-1]; \ | ||
| argsEnd--; \ | ||
| callbackProvided = true; \ | ||
| } else { \ | ||
| callback = Nan::Null(); \ | ||
| callbackProvided = false; \ | ||
| } | ||
| #define EXCEPTION_CALL_CALLBACK(JAVA, STRBUILDER) \ | ||
| std::ostringstream errStr; \ | ||
| errStr << STRBUILDER; \ | ||
| v8::Local<v8::Value> error = javaExceptionToV8(JAVA, env, errStr.str()); \ | ||
| v8::Local<v8::Value> argv[2]; \ | ||
| argv[0] = error; \ | ||
| argv[1] = Nan::Undefined(); \ | ||
| Nan::Call(callback.As<v8::Function>(), Nan::GetCurrentContext()->Global(), 2, argv); | ||
| #define END_CALLBACK_FUNCTION(MSG) \ | ||
| if(callbackProvided) { \ | ||
| info.GetReturnValue().SetUndefined(); \ | ||
| return; \ | ||
| } else { \ | ||
| std::ostringstream str; \ | ||
| str << MSG; \ | ||
| info.GetReturnValue().Set(Nan::New(str.str().c_str()).ToLocalChecked()); \ | ||
| return; \ | ||
| } | ||
| #ifndef UNUSED_VARIABLE | ||
| #define UNUSED_VARIABLE(a) a = a; | ||
| #endif | ||
| #endif |
+1
-1
@@ -10,3 +10,3 @@ { | ||
| ], | ||
| "version": "0.14.1-rtinternal-jdk11", | ||
| "version": "0.14.2-rtinternal-jdk11", | ||
| "engines": { | ||
@@ -13,0 +13,0 @@ "node": ">=7.0.0" |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
841908
17.8%59
25.53%