#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));
}