🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@rtinternal/java

Package Overview
Dependencies
Maintainers
2
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rtinternal/java - npm Package Compare versions

Comparing version
0.14.1-rtinternal-jdk11
to
0.14.2-rtinternal-jdk11
+1404
src/java.cpp
#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);
}
#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
#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
#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"