FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

#include "java.h" #include #ifdef WIN32 #else #include #endif #include "javaObject.h" #include "javaScope.h" #include "methodCallBaton.h" #include "node_NodeDynamicProxyClass.h" #include #include #include #define DYNAMIC_PROXY_JS_ERROR -4 long v8ThreadId; /*static*/ Nan::Persistent Java::s_ct; /*static*/ std::string Java::s_nativeBindingLocation; void my_sleep(int dur) { #ifdef WIN32 Sleep(dur); #else usleep(dur); #endif } long my_getThreadId() { #ifdef WIN32 return (long)GetCurrentThreadId(); #else return (long)pthread_self(); #endif } /*static*/ void Java::Init(v8::Handle target) { Nan::HandleScope scope; v8ThreadId = my_getThreadId(); v8::Local t = Nan::New(New); s_ct.Reset(t); t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(Nan::New("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); target->Set(Nan::New("Java").ToLocalChecked(), t->GetFunction()); JavaProxyObject::init(); } NAN_METHOD(Java::New) { Nan::HandleScope scope; Java *self = new Java(); self->Wrap(info.This()); self->handle()->Set(Nan::New("classpath").ToLocalChecked(), Nan::New()); self->handle()->Set(Nan::New("options").ToLocalChecked(), Nan::New()); self->handle()->Set(Nan::New("nativeBindingLocation").ToLocalChecked(), Nan::New("Not Set").ToLocalChecked()); self->handle()->Set(Nan::New("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 Java::ensureJvm() { if(!m_jvm) { v8::Local result = createJVM(&this->m_jvm, &this->m_env); assert(result->IsNull()); return result; } return Nan::Null(); } void Java::configureAsync(v8::Local& asyncOptions) { v8::Local asyncOptionsObj = asyncOptions.As(); m_SyncSuffix = "invalid"; m_AsyncSuffix = "invalid"; m_PromiseSuffix = "invalid"; doSync = false; doAsync = false; doPromise = false; v8::Local suffixValue = asyncOptionsObj->Get(Nan::New("syncSuffix").ToLocalChecked()); if (suffixValue->IsString()) { v8::Local suffix = suffixValue->ToString(); v8::String::Utf8Value utf8(suffix); m_SyncSuffix.assign(*utf8); doSync = true; } suffixValue = asyncOptionsObj->Get(Nan::New("asyncSuffix").ToLocalChecked()); if (suffixValue->IsString()) { v8::Local suffix = suffixValue->ToString(); v8::String::Utf8Value utf8(suffix); m_AsyncSuffix.assign(*utf8); doAsync = true; } suffixValue = asyncOptionsObj->Get(Nan::New("promiseSuffix").ToLocalChecked()); if (suffixValue->IsString()) { v8::Local suffix = suffixValue->ToString(); v8::String::Utf8Value utf8(suffix); m_PromiseSuffix.assign(*utf8); v8::Local promisify = asyncOptionsObj->Get(Nan::New("promisify").ToLocalChecked()); if (!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 Java::createJVM(JavaVM** jvm, JNIEnv** env) { v8::Local asyncOptions = this->handle()->Get(Nan::New("asyncOptions").ToLocalChecked()); if (asyncOptions->IsObject()) { configureAsync(asyncOptions); } // setup classpath std::ostringstream classPath; classPath handle()->Get(Nan::New("classpath").ToLocalChecked()); if(!classPathValue->IsArray()) { return Nan::TypeError("Classpath must be an array"); } v8::Local classPathArrayTemp = v8::Local::Cast(classPathValue); m_classPathArray.Reset(classPathArrayTemp); for(uint32_t i=0; iLength(); i++) { if(i != 0) { #ifdef WIN32 classPath IsString()) { return Nan::TypeError("Classpath must only contain strings"); } v8::Local arrayItem = arrayItemValue->ToString(); v8::String::Utf8Value arrayItemStr(arrayItem); classPath handle()->Get(Nan::New("nativeBindingLocation").ToLocalChecked()); v8::String::Utf8Value nativeBindingLocationStr(v8NativeBindingLocation); s_nativeBindingLocation = *nativeBindingLocationStr; // get other options v8::Local optionsValue = this->handle()->Get(Nan::New("options").ToLocalChecked()); if(!optionsValue->IsArray()) { return Nan::TypeError("options must be an array"); } v8::Local optionsArrayTemp = v8::Local::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; iLength(); i++) { v8::Local arrayItemValue = optionsArrayTemp->Get(i); if(!arrayItemValue->IsString()) { delete[] vmOptions; return Nan::TypeError("options must only contain strings"); } v8::Local arrayItem = arrayItemValue->ToString(); v8::String::Utf8Value 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 onJvmCreated = this->handle()->Get(Nan::New("onJvmCreated").ToLocalChecked()); // 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("classpath").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); Nan::SetAccessor(this->handle(), Nan::New("options").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); Nan::SetAccessor(this->handle(), Nan::New("nativeBindingLocation").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); Nan::SetAccessor(this->handle(), Nan::New("asyncOptions").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); Nan::SetAccessor(this->handle(), Nan::New("onJvmCreated").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter); if (onJvmCreated->IsFunction()) { v8::Local onJvmCreatedFunc = onJvmCreated.As(); v8::Local context = Nan::New(); onJvmCreatedFunc->Call(context, 0, NULL); } return Nan::Null(); } NAN_GETTER(Java::AccessorProhibitsOverwritingGetter) { Java* self = Nan::ObjectWrap::Unwrap(info.This()); Nan::HandleScope scope; v8::String::Utf8Value 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 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(info.This()); v8::Local 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 " run(); END_CALLBACK_FUNCTION("\"Constructor for class '" GetEnv returned " + to_string(ret); dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } Nan::HandleScope scope; v8::Array* v8Args; v8::Function* fn; v8::Handle* argv; int argc; int i; v8::Local v8Result; jobject javaResult; v8::Local dynamicProxyDataFunctions = Nan::New(dynamicProxyData->functions); v8::Local fnObj = dynamicProxyDataFunctions->Get(Nan::New(dynamicProxyData->methodName.c_str()).ToLocalChecked()); if(fnObj->IsUndefined() || fnObj->IsNull()) { dynamicProxyData->throwableClass = "java/lang/NoSuchMethodError"; dynamicProxyData->throwableMessage = "Could not find js function " + dynamicProxyData->methodName; dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } if(!fnObj->IsFunction()) { dynamicProxyData->throwableClass = "java/lang/IllegalStateException"; dynamicProxyData->throwableMessage = dynamicProxyData->methodName + " is not a function"; dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } fn = v8::Function::Cast(*fnObj); if(dynamicProxyData->args) { v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); argc = v8Args->Length(); } else { argc = 0; } argv = new v8::Handle[argc]; for(i=0; iGet(i); } v8::TryCatch tryCatch; v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); delete[] argv; if (tryCatch.HasCaught()) { dynamicProxyData->throwableClass = "node/NodeJsException"; v8::String::Utf8Value stackTrace(tryCatch.StackTrace()); if (stackTrace.length() > 0) { dynamicProxyData->throwableMessage = std::string(*stackTrace); } else { v8::String::Utf8Value exception(tryCatch.Exception()); dynamicProxyData->throwableMessage = std::string(*exception); } tryCatch.Reset(); dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } if(!dynamicProxyDataVerify(dynamicProxyData)) { return; } javaResult = v8ToJava(env, v8Result); if(javaResult == NULL) { dynamicProxyData->result = NULL; } else { dynamicProxyData->result = env->NewGlobalRef(javaResult); } dynamicProxyData->done = true; } 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) { long myThreadId = my_getThreadId(); bool hasArgsGlobalRef = false; // args needs to be global, you can't send env across thread boundaries DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; dynamicProxyData->args = args; dynamicProxyData->done = false; dynamicProxyData->result = NULL; dynamicProxyData->throwableClass = ""; dynamicProxyData->throwableMessage = ""; jclass methodClazz = env->FindClass("java/lang/reflect/Method"); jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;"); dynamicProxyData->methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName)); assertNoException(env); uv_work_t* req = new uv_work_t(); req->data = dynamicProxyData; if(myThreadId == v8ThreadId) { #if NODE_MINOR_VERSION >= 10 EIO_AfterCallJs(req, 0); #else EIO_AfterCallJs(req); #endif } else { if (args) { // if args is not null and we have to kick this across the thread boundary, make it a global ref dynamicProxyData->args = (jobjectArray) env->NewGlobalRef(args); hasArgsGlobalRef = true; } uv_queue_work(uv_default_loop(), req, EIO_CallJs, (uv_after_work_cb)EIO_AfterCallJs); while(!dynamicProxyData->done) { my_sleep(100); } } if(!dynamicProxyDataVerify(dynamicProxyData)) { throwNewThrowable(env, "java/lang/IllegalStateException", "dynamicProxyData was corrupted"); } if(hasArgsGlobalRef) { env->DeleteGlobalRef(dynamicProxyData->args); } if (dynamicProxyData->done == DYNAMIC_PROXY_JS_ERROR) { throwNewThrowable(env, dynamicProxyData->throwableClass.c_str(), dynamicProxyData->throwableMessage); } jobject result = NULL; if(dynamicProxyData->result) { // need to retain a local ref so that we can return it, otherwise the returned object gets corrupted result = env->NewLocalRef(dynamicProxyData->result); env->DeleteGlobalRef(dynamicProxyData->result); } return result; } JNIEXPORT void JNICALL Java_node_NodeDynamicProxyClass_unref(JNIEnv *env, jobject src, jlong ptr) { DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; unref(dynamicProxyData); }

Back | FazBrowse Home | New Git URL