[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/LittleGnome/node-java/master/src-cpp/utils.cpp [Back]  [Original]

#include "utils.h"
#include "java.h"
#include "javaObject.h"
#include 
#include 
#include 
#include 

#define MODIFIER_STATIC 9

jobject v8ToJava_javaObject(JNIEnv *env, v8::Local obj);
jobject v8ToJava_javaLong(JNIEnv *env, v8::Local obj);

bool hasSetFailed(Nan::Maybe v) {
  if (v.IsNothing()) {
    return false;
  }
  return v.ToChecked() == false;
}

void javaReflectionGetMethods(JNIEnv *env, jclass clazz, std::list *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 *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 *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 GetArrayLength(arr);
  for (jsize i = 0; i < count; i++) {
    if (i != 0) {
      result GetObjectArrayElement(arr, i);
    result 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 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 array, uint32_t arraySize) {
  std::set types;

  if (arraySize == 0) {
    return kObject;
  }

  for (uint32_t i = 0; i < arraySize; i++) {
    v8::Local 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 obj = v8::Local::Cast(arg);
      v8::Local isJavaLong =
          GetHiddenValue(obj, Nan::New(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 arg) {
  if (arg.IsEmpty() || arg->IsNull() || arg->IsUndefined()) {
    return NULL;
  }

  if (arg->IsArray()) {
    v8::Local array = v8::Local::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(arg).FromJust();
    jclass clazz = env->FindClass("java/lang/Integer");
    jmethodID constructor = env->GetMethodID(clazz, "", "(I)V");
    return env->NewObject(clazz, constructor, val);
  }

  if (arg->IsNumber()) {
    jdouble val = Nan::To(arg).FromJust();
    jclass clazz = env->FindClass("java/lang/Double");
    jmethodID constructor = env->GetMethodID(clazz, "", "(D)V");
    return env->NewObject(clazz, constructor, val);
  }

  if (arg->IsBoolean()) {
    jboolean val = Nan::To(arg).FromJust();
    jclass clazz = env->FindClass("java/lang/Boolean");
    jmethodID constructor = env->GetMethodID(clazz, "", "(Z)V");
    return env->NewObject(clazz, constructor, val);
  }

  if (arg->IsObject()) {
    v8::Local obj = v8::Local::Cast(arg);

    v8::Local isJavaObject =
        GetHiddenValue(obj, Nan::New(V8_HIDDEN_MARKER_JAVA_OBJECT).ToLocalChecked());
    if (!isJavaObject.IsEmpty() && isJavaObject->IsBoolean()) {
      return v8ToJava_javaObject(env, obj);
    }

    v8::Local isJavaLong =
        GetHiddenValue(obj, Nan::New(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 obj) {
  JavaObject *javaObject = Nan::ObjectWrap::Unwrap(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 obj) {
  jobject longValue = v8ToJava(
      env, obj->Get(Nan::GetCurrentContext(), Nan::New("longValue").ToLocalChecked()).ToLocalChecked());
  jclass longClazz = env->FindClass("java/lang/Long");
  jmethodID long_constructor = env->GetMethodID(longClazz, "", "(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, "", "()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, "", "(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 javaExceptionToV8(Java *java, JNIEnv *env, jthrowable ex, const std::string &alternateMessage) {
  std::ostringstream msg;
  msg 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 newArrayBuffer(void *elems, size_t length) {
  v8::Local 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 newArrayBuffer(void *elems, size_t length) {
  v8::Local ab = v8::ArrayBuffer::New(v8::Isolate::GetCurrent(), length);
  memcpy(ab->GetContents().Data(), elems, length);
  return ab;
}
#endif

v8::Local javaCharToV8String(jchar c) {
#if ((NODE_MAJOR_VERSION == 0) && (NODE_MINOR_VERSION GetCharArrayElements((jcharArray)objArray, 0);
#if (NODE_VERSION_AT_LEAST(4, 0, 0))
    size_t byteLength = arraySize * 2;
    v8::Local 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 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(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 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(elems[i]));
    }
    env->ReleaseByteArrayElements((jbyteArray)objArray, elems, 0);
#endif
  } break;

  case TYPE_BOOLEAN: {
    jboolean *elems = env->GetBooleanArrayElements((jbooleanArray)objArray, 0);
    for (jsize i = 0; i < arraySize; i++) {
      if (hasSetFailed(result->Set(Nan::GetCurrentContext(), i, Nan::New(elems[i])))) {
        return v8::Exception::Error(Nan::New("set array element failed").ToLocalChecked());
      }
    }
    env->ReleaseBooleanArrayElements((jbooleanArray)objArray, elems, 0);
  } 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 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(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 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(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 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(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]);
      if (hasSetFailed(result->Set(Nan::GetCurrentContext(), i, JavaObject::New(java, obj)))) {
        return v8::Exception::Error(Nan::New("could not set array element").ToLocalChecked());
      }
    }
    env->ReleaseLongArrayElements((jlongArray)objArray, elems, 0);
  } break;

  default:
    for (jsize i = 0; i < arraySize; i++) {
      jobject obj = env->GetObjectArrayElement(objArray, i);
      v8::Local item = javaToV8(java, env, obj);
      if (hasSetFailed(result->Set(Nan::GetCurrentContext(), i, item))) {
        return v8::Exception::Error(Nan::New("could not set array element").ToLocalChecked());
      }
    }
    break;
  }

  return result;
}

v8::Local javaToV8(Java *java, JNIEnv *env, jobject obj) { return javaToV8(java, env, obj, NULL); }

v8::Local 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 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(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(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 v8Result = Nan::New((double)result);
    v8::NumberObject *v8ResultNumberObject = v8::NumberObject::Cast(*v8Result);
    if (hasSetFailed(v8ResultNumberObject->Set(Nan::GetCurrentContext(),
                                               Nan::New("longValue").ToLocalChecked(),
                                               Nan::New(strValue.c_str()).ToLocalChecked()))) {
      return v8::Exception::Error(Nan::New("could not set longValue").ToLocalChecked());
    }
    SetHiddenValue(v8ResultNumberObject, Nan::New(V8_HIDDEN_MARKER_JAVA_LONG).ToLocalChecked(),
                   Nan::New(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);
    // FindClass("java/lang/Short");
    jmethodID short_shortValue = env->GetMethodID(shortClazz, "shortValue", "()S");
    jshort result = env->CallShortMethod(obj, short_shortValue);
    assertNoException(env);
    return Nan::New(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(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(result);
  }
  case TYPE_STRING: {
    std::string str = javaObjectToString(env, obj);
    return Nan::New(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, "", "(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 

Web Proxy Viewer  |  New URL  |  Original Page