jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) { if(arg->IsNull() || arg->IsUndefined()) { return NULL; } if(arg->IsArray()) { v8::Local<v8::Array> array = v8::Array::Cast(*arg); uint32_t arraySize = array->Length(); jclass objectClazz = env->FindClass("java/lang/Object"); jobjectArray result = env->NewObjectArray(arraySize, objectClazz, NULL); for(uint32_t i=0; i<arraySize; i++) { jobject val = v8ToJava(env, array->Get(i)); env->SetObjectArrayElement(result, i, val); } return result; } if(arg->IsString()) { v8::String::AsciiValue val(arg->ToString()); return env->NewStringUTF(*val); } if(arg->IsInt32() || arg->IsUint32()) { jint val = arg->ToInt32()->Value(); 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 = arg->ToNumber()->Value(); 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 = arg->ToBoolean()->Value(); 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::Object::Cast(*arg); v8::String::AsciiValue constructorName(obj->GetConstructorName()); if(strcmp(*constructorName, "JavaObject") == 0) { JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj); return javaObject->getObject(); } } // TODO: handle other arg types v8::String::AsciiValue typeStr(arg); printf("Unhandled type: %s\n", *typeStr); return NULL; }
jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj) { JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj); jobject jobj = javaObject->getObject(); jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass"); if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) { jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J"); DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(jobj, ptrField); if(!dynamicProxyDataVerify(proxyData)) { return NULL; } jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName); if(dynamicInterface == NULL) { printf("Could not find interface %s\n", proxyData->interfaceName.c_str()); return NULL; } jclass classClazz = env->FindClass("java/lang/Class"); jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL); env->SetObjectArrayElement(classArray, 0, dynamicInterface); jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;"); jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader); if(classLoader == NULL) { jclass objectClazz = env->FindClass("java/lang/Object"); jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;"); jobject jobjClass = env->CallObjectMethod(jobj, object_getClass); classLoader = env->CallObjectMethod(jobjClass, class_getClassLoader); } 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;"); if(classLoader == NULL) { printf("Could not get classloader for Proxy\n"); return NULL; } if(classArray == NULL) { printf("Could not create class array for Proxy\n"); return NULL; } if(jobj == NULL) { printf("Not a valid object to wrap\n"); return NULL; } jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj); } return jobj; }
JNIEXPORT void JNICALL Java_org_appcelerator_kroll_runtime_v8_V8Object_nativeRelease (JNIEnv *env, jclass clazz, jlong refPointer) { ENTER_V8(V8Runtime::globalContext); JNIScope jniScope(env); if (refPointer) { Persistent<Object> handle((Object *)refPointer); JavaObject *javaObject = NativeObject::Unwrap<JavaObject>(handle); if (javaObject && javaObject->isDetached()) { delete javaObject; } } }
// converts js value to java object and recursively converts sub objects if this // object is a container type jobject TypeConverter::jsValueToJavaObject(v8::Local<v8::Value> jsValue, bool *isNew) { JNIEnv *env = JNIScope::getEnv(); if (env == NULL) { return NULL; } if (jsValue->IsNumber()) { jdouble javaDouble = TypeConverter::jsNumberToJavaDouble(jsValue->ToNumber()); *isNew = true; return env->NewObject(JNIUtil::doubleClass, JNIUtil::doubleInitMethod, javaDouble); } else if (jsValue->IsBoolean()) { jboolean javaBoolean = TypeConverter::jsBooleanToJavaBoolean(jsValue->ToBoolean()); *isNew = true; return env->NewObject(JNIUtil::booleanClass, JNIUtil::booleanInitMethod, javaBoolean); } else if (jsValue->IsString()) { *isNew = true; return TypeConverter::jsStringToJavaString(jsValue->ToString()); } else if (jsValue->IsDate()) { Local<Date> date = Local<Date>::Cast<Value>(jsValue); return TypeConverter::jsDateToJavaDate(date); } else if (jsValue->IsArray()) { *isNew = true; return TypeConverter::jsArrayToJavaArray(v8::Handle<v8::Array>::Cast(jsValue)); } else if (jsValue->IsFunction()) { *isNew = true; return TypeConverter::jsObjectToJavaFunction(jsValue->ToObject()); } else if (jsValue->IsObject()) { v8::Handle<v8::Object> jsObject = jsValue->ToObject(); if (JavaObject::isJavaObject(jsObject)) { *isNew = JavaObject::useGlobalRefs ? false : true; JavaObject *javaObject = JavaObject::Unwrap<JavaObject>(jsObject); return javaObject->getJavaObject(); } else { v8::Handle<v8::Array> objectKeys = jsObject->GetOwnPropertyNames(); int numKeys = objectKeys->Length(); *isNew = true; jobject javaHashMap = env->NewObject(JNIUtil::hashMapClass, JNIUtil::hashMapInitMethod, numKeys); for (int i = 0; i < numKeys; i++) { v8::Local<v8::Value> jsObjectPropertyKey = objectKeys->Get((uint32_t) i); bool keyIsNew, valueIsNew; jobject javaObjectPropertyKey = TypeConverter::jsValueToJavaObject(jsObjectPropertyKey, &keyIsNew); v8::Local<v8::Value> jsObjectPropertyValue = jsObject->Get(jsObjectPropertyKey); jobject javaObjectPropertyValue = TypeConverter::jsValueToJavaObject(jsObjectPropertyValue, &valueIsNew); jobject result = env->CallObjectMethod(javaHashMap, JNIUtil::hashMapPutMethod, javaObjectPropertyKey, javaObjectPropertyValue); env->DeleteLocalRef(result); if (keyIsNew) { env->DeleteLocalRef(javaObjectPropertyKey); } if (valueIsNew) { env->DeleteLocalRef(javaObjectPropertyValue); } } return javaHashMap; } } LOGW(TAG, "jsValueToJavaObject returning null"); return NULL; }
// Callback for V8 letting us know the JavaScript object is no longer reachable. // Once we receive this callback we can safely release our strong reference // on the wrapped Java object so it can become eligible for collection. static void DetachCallback(const v8::WeakCallbackData<v8::Object, JavaObject>& data) { JavaObject* javaObject = data.GetParameter(); javaObject->detach(); }
v8::Handle<v8::Value> TypeConverter::javaObjectToJsValue(JNIEnv *env, jobject javaObject) { if (!javaObject) { return v8::Null(); } if (env->IsInstanceOf(javaObject, JNIUtil::booleanClass)) { jboolean javaBoolean = env->CallBooleanMethod(javaObject, JNIUtil::booleanBooleanValueMethod); return javaBoolean ? v8::True() : v8::False(); } else if (env->IsInstanceOf(javaObject, JNIUtil::numberClass)) { jdouble javaDouble = env->CallDoubleMethod(javaObject, JNIUtil::numberDoubleValueMethod); return v8::Number::New((double) javaDouble); } else if (env->IsInstanceOf(javaObject, JNIUtil::stringClass)) { return TypeConverter::javaStringToJsString(env, (jstring) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::dateClass)) { return TypeConverter::javaDateToJsDate(env, javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::hashMapClass)) { return TypeConverter::javaHashMapToJsValue(env, javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::krollProxyClass)) { jobject krollObject = env->GetObjectField(javaObject, JNIUtil::krollProxyKrollObjectField); if (krollObject) { jlong v8ObjectPointer = env->GetLongField(krollObject, JNIUtil::v8ObjectPtrField); env->DeleteLocalRef(krollObject); if (v8ObjectPointer != 0) { Persistent<Object> v8Object = Persistent<Object>((Object *) v8ObjectPointer); JavaObject *jo = NativeObject::Unwrap<JavaObject>(v8Object); jobject javaProxy = jo->getJavaObject(); // Called to explicitly go from weak reference to strong! if (!JavaObject::useGlobalRefs) { // But then we need to delete the local reference to avoid JNI ref leak! env->DeleteLocalRef(javaProxy); } return v8Object; } } jclass javaObjectClass = env->GetObjectClass(javaObject); v8::Handle<v8::Object> proxyHandle = ProxyFactory::createV8Proxy(javaObjectClass, javaObject); env->DeleteLocalRef(javaObjectClass); return proxyHandle; } else if (env->IsInstanceOf(javaObject, JNIUtil::v8FunctionClass)) { return javaObjectToJsFunction(javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::objectArrayClass)) { return javaArrayToJsArray((jobjectArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::shortArrayClass)) { return javaArrayToJsArray((jshortArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::intArrayClass)) { return javaArrayToJsArray((jintArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::longArrayClass)) { return javaArrayToJsArray((jlongArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::floatArrayClass)) { return javaArrayToJsArray((jfloatArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::doubleArrayClass)) { return javaArrayToJsArray((jdoubleArray) javaObject); } else if (env->IsInstanceOf(javaObject, JNIUtil::booleanArrayClass)) { return javaArrayToJsArray((jbooleanArray) javaObject); } else if (env->IsSameObject(JNIUtil::undefinedObject, javaObject)) { return v8::Undefined(); } jclass javaObjectClass = env->GetObjectClass(javaObject); JNIUtil::logClassName("!!! Unable to convert unknown Java object class '%s' to JS value !!!", javaObjectClass, true); env->DeleteLocalRef(javaObjectClass); return v8::Handle<v8::Value>(); }
jobject TypeConverter::jsValueToJavaObject(JNIEnv *env, v8::Local<v8::Value> jsValue, bool *isNew) { if (jsValue->IsNumber()) { *isNew = true; if (jsValue->IsInt32()) { jint javaInt = TypeConverter::jsNumberToJavaInt(jsValue->ToNumber()); return env->NewObject(JNIUtil::integerClass, JNIUtil::integerInitMethod, javaInt); } jdouble javaDouble = TypeConverter::jsNumberToJavaDouble(jsValue->ToNumber()); return env->NewObject(JNIUtil::doubleClass, JNIUtil::doubleInitMethod, javaDouble); } else if (jsValue->IsBoolean()) { jboolean javaBoolean = TypeConverter::jsBooleanToJavaBoolean(jsValue->ToBoolean()); *isNew = true; return env->NewObject(JNIUtil::booleanClass, JNIUtil::booleanInitMethod, javaBoolean); } else if (jsValue->IsString()) { *isNew = true; return TypeConverter::jsStringToJavaString(env, jsValue->ToString()); } else if (jsValue->IsDate()) { Local<Date> date = Local<Date>::Cast<Value>(jsValue); return TypeConverter::jsDateToJavaDate(env, date); } else if (jsValue->IsArray()) { *isNew = true; return TypeConverter::jsArrayToJavaArray(env, v8::Handle<v8::Array>::Cast(jsValue)); } else if (jsValue->IsFunction()) { *isNew = true; return TypeConverter::jsObjectToJavaFunction(env, jsValue->ToObject()); } else if (jsValue->IsObject()) { v8::Handle<v8::Object> jsObject = jsValue->ToObject(); if (JavaObject::isJavaObject(jsObject)) { *isNew = JavaObject::useGlobalRefs ? false : true; JavaObject *javaObject = JavaObject::Unwrap<JavaObject>(jsObject); return javaObject->getJavaObject(); } else { // Unwrap hyperloop JS wrappers to get native java proxy Handle<String> nativeString = String::New("$native"); if (jsObject->HasOwnProperty(nativeString)) { v8::Local<v8::Value> nativeObject = jsObject->GetRealNamedProperty(nativeString); jsObject = nativeObject->ToObject(); if (JavaObject::isJavaObject(jsObject)) { *isNew = JavaObject::useGlobalRefs ? false : true; JavaObject *javaObject = JavaObject::Unwrap<JavaObject>(jsObject); return javaObject->getJavaObject(); } } v8::Handle<v8::Array> objectKeys = jsObject->GetOwnPropertyNames(); int numKeys = objectKeys->Length(); *isNew = true; jobject javaHashMap = env->NewObject(JNIUtil::hashMapClass, JNIUtil::hashMapInitMethod, numKeys); for (int i = 0; i < numKeys; i++) { v8::Local<v8::Value> jsObjectPropertyKey = objectKeys->Get((uint32_t) i); bool keyIsNew, valueIsNew; jobject javaObjectPropertyKey = TypeConverter::jsValueToJavaObject(env, jsObjectPropertyKey, &keyIsNew); v8::Local<v8::Value> jsObjectPropertyValue = jsObject->Get(jsObjectPropertyKey); jobject javaObjectPropertyValue = TypeConverter::jsValueToJavaObject(env, jsObjectPropertyValue, &valueIsNew); jobject result = env->CallObjectMethod(javaHashMap, JNIUtil::hashMapPutMethod, javaObjectPropertyKey, javaObjectPropertyValue); env->DeleteLocalRef(result); if (keyIsNew) { env->DeleteLocalRef(javaObjectPropertyKey); } if (valueIsNew) { env->DeleteLocalRef(javaObjectPropertyValue); } } return javaHashMap; } } if (!jsValue->IsNull() && !jsValue->IsUndefined()) { LOGW(TAG, "jsValueToJavaObject returning null."); } return NULL; }
// Callback for V8 letting us know the JavaScript object is no longer reachable. // Once we receive this callback we can safely release our strong reference // on the wrapped Java object so it can become eligible for collection. static void DetachCallback(v8::Persistent<v8::Value> value, void *data) { JavaObject *javaObject = static_cast<JavaObject*>(data); javaObject->detach(); }
/*static*/ v8::Local<v8::Object> JavaObject::New(Java *java, jobject obj) { v8::HandleScope scope; JNIEnv *env = java->getJavaEnv(); obj = env->NewGlobalRef(obj); 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); 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::Persistent<v8::FunctionTemplate> persistentFuncTemplate; if(sFunctionTemplates.find(className) != sFunctionTemplates.end()) { //printf("existing className: %s\n", className.c_str()); persistentFuncTemplate = sFunctionTemplates[className]; } else { //printf("create className: %s\n", className.c_str()); v8::Local<v8::FunctionTemplate> funcTemplate = v8::FunctionTemplate::New(); funcTemplate->InstanceTemplate()->SetInternalFieldCount(1); funcTemplate->SetClassName(v8::String::NewSymbol(className.c_str())); 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); std::string methodNameStr = javaToString(env, methodNameJava); v8::Handle<v8::String> methodName = v8::String::New(methodNameStr.c_str()); v8::Local<v8::FunctionTemplate> methodCallTemplate = v8::FunctionTemplate::New(methodCall, methodName); funcTemplate->PrototypeTemplate()->Set(methodName, methodCallTemplate->GetFunction()); v8::Handle<v8::String> methodNameSync = v8::String::New((methodNameStr + "Sync").c_str()); v8::Local<v8::FunctionTemplate> methodCallSyncTemplate = v8::FunctionTemplate::New(methodCallSync, methodName); funcTemplate->PrototypeTemplate()->Set(methodNameSync, methodCallSyncTemplate->GetFunction()); } 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); std::string fieldNameStr = javaToString(env, fieldNameJava); v8::Handle<v8::String> fieldName = v8::String::New(fieldNameStr.c_str()); funcTemplate->InstanceTemplate()->SetAccessor(fieldName, fieldGetter, fieldSetter); } sFunctionTemplates[className] = persistentFuncTemplate = v8::Persistent<v8::FunctionTemplate>::New(funcTemplate); } v8::Local<v8::Function> ctor = persistentFuncTemplate->GetFunction(); v8::Local<v8::Object> javaObjectObj = ctor->NewInstance(); javaObjectObj->SetHiddenValue(v8::String::New("__isJavaObject"), v8::Boolean::New(true)); JavaObject *self = new JavaObject(java, obj); self->Wrap(javaObjectObj); return scope.Close(javaObjectObj); }