#include "javaObject.h" #include "java.h" #include "javaScope.h" #include "utils.h" #include #include /*static*/ std::map<:string nan::persistent>*> JavaObject::sFunctionTemplates; /*static*/ void JavaObject::Init(v8::Handle<:object> target) { } /*static*/ v8::Local<: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<:function> promisify; if(java->DoPromise()) { v8::Local<:object> asyncOptions = java->handle()->Get(Nan::New<:string>("asyncOptions").ToLocalChecked()).As<:object>(); v8::Local<:value> promisifyValue = asyncOptions->Get(Nan::New<:string>("promisify").ToLocalChecked()); promisify = promisifyValue.As<:function>(); } v8::Local<: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<:functiontemplate>(); funcTemplate->InstanceTemplate()->SetInternalFieldCount(1); funcTemplate->SetClassName(Nan::New<:string>(className.c_str()).ToLocalChecked()); // copy methods to template std::list 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::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<:string> baseMethodName = Nan::New<:string>(methodNameStr.c_str()).ToLocalChecked(); std::string methodNameAsyncStr = methodNameStr; const char* methodNameAsync = methodNameAsyncStr.append(java->AsyncSuffix()).c_str(); v8::Local<:functiontemplate> methodCallTemplate = Nan::New<:functiontemplate>(methodCall, baseMethodName); Nan::SetPrototypeTemplate(funcTemplate, methodNameAsync, methodCallTemplate); std::string methodNameSyncStr = methodNameStr; const char* methodNameSync = methodNameSyncStr.append(java->SyncSuffix()).c_str(); v8::Local<:functiontemplate> methodCallSyncTemplate = Nan::New<:functiontemplate>(methodCallSync, baseMethodName); Nan::SetPrototypeTemplate(funcTemplate, methodNameSync, methodCallSyncTemplate); if (java->DoPromise()) { v8::Local<:object> recv = Nan::New<:object>(); v8::Local<:value> argv[] = { methodCallTemplate->GetFunction() }; v8::Local<:value> result = promisify->Call(recv, 1, argv); if (!result->IsFunction()) { fprintf(stderr, "Promisified result is not a function -- asyncOptions.promisify must return a function.\n"); assert(result->IsFunction()); } v8::Local<:function> promFunction = result.As<:function>(); v8::Local<:functiontemplate> promFunctionTemplate = Nan::New<: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 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::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<:string> fieldName = Nan::New<: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<:string> fieldName = Nan::New<:string>("length").ToLocalChecked(); Nan::SetAccessor(funcTemplate->InstanceTemplate(), fieldName, fieldGetter, NULL); Nan::SetIndexedPropertyHandler(funcTemplate->InstanceTemplate(), indexGetter); } Nan::Persistent<:functiontemplate>* persistentFuncTemplate = new Nan::Persistent<:functiontemplate>(); persistentFuncTemplate->Reset(funcTemplate); sFunctionTemplates[className] = persistentFuncTemplate; } v8::Local<:function> ctor = Nan::GetFunction(funcTemplate).ToLocalChecked(); v8::Local<:object> javaObjectObj = Nan::NewInstance(ctor).ToLocalChecked(); SetHiddenValue(javaObjectObj, Nan::New<:string>(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked(), Nan::New<: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(info.This()); JNIEnv *env = self->m_java->getJavaEnv(); JavaScope javaScope(env); v8::String::Utf8Value 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 '" (info.This()); JNIEnv *env = self->m_java->getJavaEnv(); JavaScope javaScope(env); v8::String::Utf8Value 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<:value> ex = javaExceptionToV8(self->m_java, env, msg); Nan::ThrowError(ex); return; } // run v8::Local<:value> callback = Nan::Undefined(); InstanceMethodCallBaton* baton = new InstanceMethodCallBaton(self->m_java, self, method, methodArgs, callback); v8::Local<: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<:function> fn = info.Data().As<:function>(); v8::Handle<:value>* argv = new v8::Handle<:value>[info.Length()]; for (int i = 0 ; i result = fn->Call(info.This(), info.Length(), argv); delete[] argv; info.GetReturnValue().Set(result); } NAN_GETTER(JavaObject::fieldGetter) { Nan::HandleScope scope; JavaObject* self = Nan::ObjectWrap::Unwrap(info.This()); JNIEnv *env = self->m_java->getJavaEnv(); JavaScope javaScope(env); v8::String::Utf8Value 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 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 ex = javaExceptionToV8(self->m_java, env, errStr.str()); Nan::ThrowError(ex); return; } v8::Local<:value> result = javaToV8(self->m_java, env, val); info.GetReturnValue().Set(result); } NAN_SETTER(JavaObject::fieldSetter) { Nan::HandleScope scope; JavaObject* self = Nan::ObjectWrap::Unwrap(info.This()); JNIEnv *env = self->m_java->getJavaEnv(); JavaScope javaScope(env); jobject newValue = v8ToJava(env, value); v8::String::Utf8Value propertyCStr(property); std::string propertyStr = *propertyCStr; jobject field = javaFindField(env, self->m_class, propertyStr); if(field == NULL) { std::ostringstream errStr; errStr 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 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(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<:value> result = javaToV8(self->m_java, env, item); info.GetReturnValue().Set(result); } /*static*/ Nan::Persistent<:functiontemplate> JavaProxyObject::s_proxyCt; /*static*/ void JavaProxyObject::init() { v8::Local<:functiontemplate> t = Nan::New<:functiontemplate>(); s_proxyCt.Reset(t); t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(Nan::New<:string>("NodeDynamicProxy").ToLocalChecked()); Nan::SetPrototypeTemplate(t, "unref", Nan::New<:functiontemplate>(doUnref)); v8::Local<:string> fieldName = Nan::New<:string>("invocationHandler").ToLocalChecked(); Nan::SetAccessor(t->InstanceTemplate(), fieldName, invocationHandlerGetter); } v8::Local<:object> JavaProxyObject::New(Java *java, jobject obj, DynamicProxyData* dynamicProxyData) { Nan::EscapableHandleScope scope; v8::Local<:function> ctor = Nan::New(s_proxyCt)->GetFunction(); v8::Local<:object> javaObjectObj = Nan::NewInstance(ctor).ToLocalChecked(); SetHiddenValue(javaObjectObj, Nan::New<:string>(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked(), Nan::New<: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(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(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)); }