ObjectManager::ObjectManager() : m_numberOfGC(0), m_currentObjectId(0), m_cache(NewWeakGlobalRefCallback, DeleteWeakGlobalRefCallback, 1000, this) { JEnv env; PlatformClass = env.FindClass("com/tns/Platform"); assert(PlatformClass != nullptr); GET_JAVAOBJECT_BY_ID_METHOD_ID = env.GetStaticMethodID(PlatformClass, "getJavaObjectByID", "(I)Ljava/lang/Object;"); assert(GET_JAVAOBJECT_BY_ID_METHOD_ID != nullptr); GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID = env.GetStaticMethodID(PlatformClass, "getOrCreateJavaObjectID", "(Ljava/lang/Object;)I"); assert(GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID != nullptr); MAKE_INSTANCE_WEAK_BATCH_METHOD_ID = env.GetStaticMethodID(PlatformClass, "makeInstanceWeak", "(Ljava/nio/ByteBuffer;IZ)V"); assert(MAKE_INSTANCE_WEAK_BATCH_METHOD_ID != nullptr); CHECK_WEAK_OBJECTS_ARE_ALIVE_METHOD_ID = env.GetStaticMethodID(PlatformClass, "checkWeakObjectAreAlive", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;I)V"); assert(CHECK_WEAK_OBJECTS_ARE_ALIVE_METHOD_ID != nullptr); JAVA_LANG_CLASS = env.FindClass("java/lang/Class"); assert(JAVA_LANG_CLASS != nullptr); GET_NAME_METHOD_ID = env.GetMethodID(JAVA_LANG_CLASS, "getName", "()Ljava/lang/String;"); assert(GET_NAME_METHOD_ID != nullptr); ObjectManager::instance = this; }
bool NativeScriptRuntime::RegisterInstance(const Local<Object>& jsObject, const std::string& fullClassName, const ArgsWrapper& argWrapper, const Local<Object>& implementationObject, bool isInterface) { bool success; DEBUG_WRITE("RegisterInstance called for '%s'", fullClassName.c_str()); JEnv env; jclass generatedJavaClass = ResolveClass(fullClassName, implementationObject); int javaObjectID = objectManager->GenerateNewObjectID(); DEBUG_WRITE("RegisterInstance: Linking new instance"); objectManager->Link(jsObject, javaObjectID, nullptr); jobject instance = CreateJavaInstance(javaObjectID, fullClassName, argWrapper, generatedJavaClass, isInterface); JniLocalRef localInstance(instance); success = !localInstance.IsNull(); if (success) { DEBUG_WRITE("RegisterInstance: Updating linked instance with its real class"); jclass instanceClass = env.FindClass(fullClassName); objectManager->SetJavaClass(jsObject, instanceClass); } else { DEBUG_WRITE("RegisterInstance failed with null new instance"); } return success; }
void NativeScriptRuntime::Init(JavaVM *jvm, ObjectManager *objectManager) { NativeScriptRuntime::jvm = jvm; JEnv env; JAVA_LANG_STRING = env.FindClass("java/lang/String"); assert(JAVA_LANG_STRING != nullptr); PlatformClass = env.FindClass("com/tns/Platform"); assert(PlatformClass != nullptr); RESOLVE_CLASS_METHOD_ID = env.GetStaticMethodID(PlatformClass, "resolveClass", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Class;"); assert(RESOLVE_CLASS_METHOD_ID != nullptr); CREATE_INSTANCE_METHOD_ID = env.GetStaticMethodID(PlatformClass, "createInstance", "([Ljava/lang/Object;II)Ljava/lang/Object;"); assert(CREATE_INSTANCE_METHOD_ID != nullptr); CACHE_CONSTRUCTOR_METHOD_ID = env.GetStaticMethodID(PlatformClass, "cacheConstructor", "(Ljava/lang/Class;[Ljava/lang/Object;)I"); assert(CACHE_CONSTRUCTOR_METHOD_ID != nullptr); GET_TYPE_METADATA = env.GetStaticMethodID(PlatformClass, "getTypeMetadata", "(Ljava/lang/String;I)[Ljava/lang/String;"); assert(GET_TYPE_METADATA != nullptr); ENABLE_VERBOSE_LOGGING_METHOD_ID = env.GetStaticMethodID(PlatformClass, "enableVerboseLogging", "()V"); assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); DISABLE_VERBOSE_LOGGING_METHOD_ID = env.GetStaticMethodID(PlatformClass, "disableVerboseLogging", "()V"); assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID = env.GetStaticMethodID(PlatformClass, "getChangeInBytesOfUsedMemory", "()J"); assert(GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID != nullptr); MetadataNode::Init(objectManager); NativeScriptRuntime::objectManager = objectManager; fieldAccessor.Init(jvm, objectManager); arrayElementAccessor.Init(jvm, objectManager); JavaObjectArrayCache::Init(jvm); MethodCache::Init(); }
void Module::Init(Isolate *isolate) { JEnv env; MODULE_CLASS = env.FindClass("com/tns/Module"); assert(MODULE_CLASS != nullptr); RESOLVE_PATH_METHOD_ID = env.GetStaticMethodID(MODULE_CLASS, "resolvePath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); assert(RESOLVE_PATH_METHOD_ID != nullptr); string requireFactoryScript = "(function () { " " function require_factory(requireInternal, dirName) { " " return function require(modulePath) { " " if(global.__requireOverride) { " " var result = global.__requireOverride(modulePath, dirName); " " if(result) { " " return result; " " } " " } " " return requireInternal(modulePath, dirName); " " } " " } " " return require_factory; " "})()"; auto source = ConvertToV8String(requireFactoryScript); auto context = isolate->GetCurrentContext(); Local<Script> script; auto maybeScript = Script::Compile(context, source).ToLocal(&script); assert(!script.IsEmpty()); Local<Value> result; auto maybeResult = script->Run(context).ToLocal(&result); assert(!result.IsEmpty() && result->IsFunction()); auto requireFactoryFunction = result.As<Function>(); auto cache = GetCache(isolate); cache->RequireFactoryFunction = new Persistent<Function>(isolate, requireFactoryFunction); auto requireFuncTemplate = FunctionTemplate::New(isolate, RequireCallback); auto requireFunc = requireFuncTemplate->GetFunction(); cache->RequireFunction = new Persistent<Function>(isolate, requireFunc); auto global = isolate->GetCurrentContext()->Global(); auto globalRequire = GetRequireFunction(isolate, Constants::APP_ROOT_FOLDER_PATH); global->Set(ConvertToV8String("require"), globalRequire); }
void NativeScriptRuntime::DumpReferenceTablesMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { JEnv env; jclass vmDbgClass = env.FindClass("dalvik/system/VMDebug"); if (vmDbgClass != nullptr) { jmethodID mid = env.GetStaticMethodID(vmDbgClass, "dumpReferenceTables", "()V"); if (mid != 0) { env.CallStaticVoidMethod(vmDbgClass, mid); } } }
bool NativeScriptRuntime::RegisterInstance(const Handle<Object>& jsObject, const std::string& fullClassName, const ArgsWrapper& argWrapper, const Handle<Object>& implementationObject, bool isInterface) { bool success; DEBUG_WRITE("RegisterInstance called for '%s'", fullClassName.c_str()); if(fullClassName == "java/lang/Object_") { int a = 5; } JEnv env; // get class from implementation object if you can jclass generatedJavaClass = nullptr; bool classIsResolved = false; if (!implementationObject.IsEmpty()) { Local < Value > val = implementationObject->GetHiddenValue(ConvertToV8String(fullClassName)); if (!val.IsEmpty()) { void* voidPointerToVal = val.As<External>()->Value(); generatedJavaClass = reinterpret_cast<jclass>(voidPointerToVal); classIsResolved = true; } } if(!classIsResolved) { generatedJavaClass = ResolveClass(fullClassName, implementationObject); } int javaObjectID = objectManager->GenerateNewObjectID(); DEBUG_WRITE("RegisterInstance: Linking new instance"); objectManager->Link(jsObject, javaObjectID, nullptr); jobject instance = CreateJavaInstance(javaObjectID, fullClassName, argWrapper, generatedJavaClass, isInterface); JniLocalRef localInstance(instance); success = !localInstance.IsNull(); if (success) { DEBUG_WRITE("RegisterInstance: Updating linked instance with its real class"); jclass instanceClass = env.FindClass(fullClassName); objectManager->SetJavaClass(jsObject, instanceClass); } else { DEBUG_WRITE("RegisterInstance failed with null new instance"); } return success; }
void NativePlatform::AppInitCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { try { auto isolate = Isolate::GetCurrent(); if (args.Length() != 1) { throw NativeScriptException(string("Application should be initialized with single parameter")); } if (!args[0]->IsObject()) { throw NativeScriptException(string("Application should be initialized with single object parameter containing overridden methods")); } // TODO: find another way to get "com/tns/NativeScriptApplication" metadata (move it to more appropriate place) auto node = MetadataNode::GetOrCreate("com/tns/NativeScriptApplication"); auto appInstance = node->CreateJSWrapper(isolate); DEBUG_WRITE("Application object created id: %d", appInstance->GetIdentityHash()); auto implementationObject = args[0]->ToObject(); implementationObject->SetHiddenValue(V8StringConstants::GetClassImplementationObject(), External::New(isolate, node)); DEBUG_WRITE("Application object implementation object is with id: %d", implementationObject->GetIdentityHash()); implementationObject->SetPrototype(appInstance->GetPrototype()); bool appSuccess = appInstance->SetPrototype(implementationObject); if (!appSuccess) { throw NativeScriptException(string("Application could not be initialized correctly")); } jweak applicationObject = g_objectManager->GetJavaObjectByID(AppJavaObjectID); JEnv env; jclass appClass = env.FindClass("com/tns/NativeScriptApplication"); g_objectManager->Link(appInstance, AppJavaObjectID, appClass); JniLocalRef applicationClass(env.GetObjectClass(applicationObject)); jmethodID setNativeScriptOverridesMethodId = env.GetMethodID((jclass)applicationClass, "setNativeScriptOverrides", "([Ljava/lang/String;)V"); jobjectArray methodOverrides = NativeScriptRuntime::GetMethodOverrides(env, implementationObject); env.CallVoidMethod(applicationObject, setNativeScriptOverridesMethodId, methodOverrides); } catch (NativeScriptException& e) { e.ReThrowToV8(); } catch (exception e) { DEBUG_WRITE("Error: c++ exception: %s", e.what()); } catch (...) { DEBUG_WRITE("Error: c++ exception!"); } }
Local<Object> ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const string& typeName, jclass clazz) { auto isolate = Isolate::GetCurrent(); auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; auto node = MetadataNode::GetOrCreate(className); auto jsWrapper = node->CreateJSWrapper(isolate); JEnv env; auto claz = env.FindClass(className); Link(jsWrapper, javaObjectID, claz); return jsWrapper; }
void JsDebugger::Init(v8::Isolate *isolate, const string& packageName, jobject jsDebugger) { s_isolate = isolate; s_packageName = packageName; JEnv env; s_jsDebugger = env.NewGlobalRef(jsDebugger); s_JsDebuggerClass = env.FindClass("com/tns/JsDebugger"); assert(s_JsDebuggerClass != nullptr); s_EnqueueMessage = env.GetMethodID(s_JsDebuggerClass, "enqueueMessage", "(Ljava/lang/String;)V"); assert(s_EnqueueMessage != nullptr); s_EnableAgent = env.GetMethodID(s_JsDebuggerClass, "enableAgent", "()V"); assert(s_EnableAgent != nullptr); }
Local<Object> NativeScriptRuntime::FindClass(const string& className) { Local<Object> clazz; JEnv env; jclass c = env.FindClass(className); if (env.ExceptionCheck() == JNI_FALSE) { jint javaObjectID = objectManager->GetOrCreateObjectId(c); clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); if (clazz.IsEmpty()) { clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", c); } } return clazz; }
void NativeScriptRuntime::DumpReferenceTablesMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { try { JEnv env; jclass vmDbgClass = env.FindClass("dalvik/system/VMDebug"); if (vmDbgClass != nullptr) { jmethodID mid = env.GetStaticMethodID(vmDbgClass, "dumpReferenceTables", "()V"); if (mid != 0) { env.CallStaticVoidMethod(vmDbgClass, mid); } } } catch (NativeScriptException& e) { e.ReThrowToV8(); } catch (exception e) { DEBUG_WRITE("Error: c++ exception: %s", e.what()); } catch (...) { DEBUG_WRITE("Error: c++ exception!"); } }
void FieldAccessor::SetJavaField(Isolate *isolate, const Local<Object>& target, const Local<Value>& value, FieldCallbackData *fieldData) { JEnv env; HandleScope handleScope(isolate); auto runtime = Runtime::GetRuntime(isolate); auto objectManager = runtime->GetObjectManager(); JniLocalRef targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); if (targetJavaObject.IsNull()) { stringstream ss; ss << "Cannot access property '" << fieldData->name << "' because there is no corresponding Java object"; throw NativeScriptException(ss.str()); } } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { //TODO: validate value is a boolean before calling if (isStatic) { env.SetStaticBooleanField(clazz, fieldId, value->BooleanValue()); } else { env.SetBooleanField(targetJavaObject, fieldId, value->BooleanValue()); } break; } case 'B': //byte { //TODO: validate value is a byte before calling if (isStatic) { env.SetStaticByteField(clazz, fieldId, value->Int32Value()); } else { env.SetByteField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'C': //char { //TODO: validate value is a single char String::Utf8Value stringValue(value->ToString()); JniLocalRef value(env.NewStringUTF(*stringValue)); const char* chars = env.GetStringUTFChars(value, 0); if (isStatic) { env.SetStaticCharField(clazz, fieldId, chars[0]); } else { env.SetCharField(targetJavaObject, fieldId, chars[0]); } env.ReleaseStringUTFChars(value, chars); break; } case 'S': //short { //TODO: validate value is a short before calling if (isStatic) { env.SetStaticShortField(clazz, fieldId, value->Int32Value()); } else { env.SetShortField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'I': //int { //TODO: validate value is a int before calling if (isStatic) { env.SetStaticIntField(clazz, fieldId, value->Int32Value()); } else { env.SetIntField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'J': //long { jlong longValue = static_cast<jlong>(ArgConverter::ConvertToJavaLong(value)); if (isStatic) { env.SetStaticLongField(clazz, fieldId, longValue); } else { env.SetLongField(targetJavaObject, fieldId, longValue); } break; } case 'F': //float { if (isStatic) { env.SetStaticFloatField(clazz, fieldId, static_cast<jfloat>(value->NumberValue())); } else { env.SetFloatField(targetJavaObject, fieldId, static_cast<jfloat>(value->NumberValue())); } break; } case 'D': //double { if (isStatic) { env.SetStaticDoubleField(clazz, fieldId, value->NumberValue()); } else { env.SetDoubleField(targetJavaObject, fieldId, value->NumberValue()); } break; } default: { stringstream ss; ss << "(InternalError): in FieldAccessor::SetJavaField: Unknown field type: '" << fieldTypeName[0] << "'"; throw NativeScriptException(ss.str()); } } } else { bool isString = fieldTypeName == "java/lang/String"; JniLocalRef result; if (!value->IsNull()) { if (isString) { //TODO: validate valie is a string; result = ConvertToJavaString(value); } else { auto objectWithHiddenID = value->ToObject(); result = objectManager->GetJavaObjectByJsObject(objectWithHiddenID); } } if (isStatic) { env.SetStaticObjectField(clazz, fieldId, result); } else { env.SetObjectField(targetJavaObject, fieldId, result); } } }
Local<Value> FieldAccessor::GetJavaField(Isolate *isolate, const Local<Object>& target, FieldCallbackData *fieldData) { JEnv env; EscapableHandleScope handleScope(isolate); auto runtime = Runtime::GetRuntime(isolate); auto objectManager = runtime->GetObjectManager(); Local<Value> fieldResult; JniLocalRef targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); if (targetJavaObject.IsNull()) { stringstream ss; ss << "Cannot access property '" << fieldData->name << "' because there is no corresponding Java object"; throw NativeScriptException(ss.str()); } } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { jboolean result; if (isStatic) { result = env.GetStaticBooleanField(clazz, fieldId); } else { result = env.GetBooleanField(targetJavaObject, fieldId); } fieldResult = Boolean::New(isolate, (result == JNI_TRUE)); break; } case 'B': //byte { jbyte result; if (isStatic) { result = env.GetStaticByteField(clazz, fieldId); } else { result = env.GetByteField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'C': //char { jchar result; if (isStatic) { result = env.GetStaticCharField(clazz, fieldId); } else { result = env.GetCharField(targetJavaObject, fieldId); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); fieldResult = handleScope.Escape(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case 'S': //short { jshort result; if (isStatic) { result = env.GetStaticShortField(clazz, fieldId); } else { result = env.GetShortField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'I': //int { jint result; if (isStatic) { result = env.GetStaticIntField(clazz, fieldId); } else { result = env.GetIntField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'J': //long { jlong result; if (isStatic) { result = env.GetStaticLongField(clazz, fieldId); } else { result = env.GetLongField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(ArgConverter::ConvertFromJavaLong(isolate, result)); break; } case 'F': //float { jfloat result; if (isStatic) { result = env.GetStaticFloatField(clazz, fieldId); } else { result = env.GetFloatField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Number::New(isolate, (double) result)); break; } case 'D': //double { jdouble result; if (isStatic) { result = env.GetStaticDoubleField(clazz, fieldId); } else { result = env.GetDoubleField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Number::New(isolate, result)); break; } default: { stringstream ss; ss << "(InternalError): in FieldAccessor::GetJavaField: Unknown field type: '" << fieldTypeName[0] << "'"; throw NativeScriptException(ss.str()); } } } else { jobject result; if (isStatic) { result = env.GetStaticObjectField(clazz, fieldId); } else { result = env.GetObjectField(targetJavaObject, fieldId); } if (result != nullptr) { bool isString = fieldTypeName == "java/lang/String"; if (isString) { auto resultV8Value = ArgConverter::jstringToV8String((jstring) result); fieldResult = handleScope.Escape(resultV8Value); } else { int javaObjectID = objectManager->GetOrCreateObjectId(result); auto objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, fieldTypeName, result); } fieldResult = handleScope.Escape(objectResult); } env.DeleteLocalRef(result); } else { fieldResult = handleScope.Escape(Null(isolate)); } } return fieldResult; }
void FieldAccessor::SetJavaField(const Local<Object>& target, const Local<Value>& value, FieldCallbackData *fieldData) { JEnv env; auto isolate = Isolate::GetCurrent(); HandleScope handleScope(isolate); jweak targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { //TODO: validate value is a boolean before calling if (isStatic) { env.SetStaticBooleanField(clazz, fieldId, value->BooleanValue()); } else { env.SetBooleanField(targetJavaObject, fieldId, value->BooleanValue()); } break; } case 'B': //byte { //TODO: validate value is a byte before calling if (isStatic) { env.SetStaticByteField(clazz, fieldId, value->Int32Value()); } else { env.SetByteField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'C': //char { //TODO: validate value is a single char String::Utf8Value stringValue(value->ToString()); JniLocalRef value(env.NewStringUTF(*stringValue)); const char* chars = env.GetStringUTFChars(value, 0); if (isStatic) { env.SetStaticCharField(clazz, fieldId, chars[0]); } else { env.SetCharField(targetJavaObject, fieldId, chars[0]); } env.ReleaseStringUTFChars(value, chars); break; } case 'S': //short { //TODO: validate value is a short before calling if (isStatic) { env.SetStaticShortField(clazz, fieldId, value->Int32Value()); } else { env.SetShortField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'I': //int { //TODO: validate value is a int before calling if (isStatic) { env.SetStaticIntField(clazz, fieldId, value->Int32Value()); } else { env.SetIntField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'J': //long { jlong longValue = static_cast<jlong>(ArgConverter::ConvertToJavaLong(value)); if (isStatic) { env.SetStaticLongField(clazz, fieldId, longValue); } else { env.SetLongField(targetJavaObject, fieldId, longValue); } break; } case 'F': //float { if (isStatic) { env.SetStaticFloatField(clazz, fieldId, static_cast<jfloat>(value->NumberValue())); } else { env.SetFloatField(targetJavaObject, fieldId, static_cast<jfloat>(value->NumberValue())); } break; } case 'D': //double { if (isStatic) { env.SetStaticDoubleField(clazz, fieldId, value->NumberValue()); } else { env.SetDoubleField(targetJavaObject, fieldId, value->NumberValue()); } break; } default: { // TODO: ASSERT_FAIL("Unknown field type"); break; } } } else { bool isString = fieldTypeName == "java/lang/String"; jobject result = nullptr; if(!value->IsNull()) { if (isString) { //TODO: validate valie is a string; result = ConvertToJavaString(value); } else { auto objectWithHiddenID = value->ToObject(); result =objectManager->GetJavaObjectByJsObject(objectWithHiddenID); } } if (isStatic) { env.SetStaticObjectField(clazz, fieldId, result); } else { env.SetObjectField(targetJavaObject, fieldId, result); } if (isString) { env.DeleteLocalRef(result); } } }
void NativeScriptRuntime::CallJavaMethod(const Local<Object>& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); JEnv env; jclass clazz; jmethodID mid; string *sig = nullptr; string *returnType = nullptr; auto retType = MethodReturnType::Unknown; MethodCache::CacheMethodInfo mi; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { clazz = env.FindClass(className); if (clazz == nullptr) { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); clazz = env.FindClass(callerClassName); if (clazz == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } entry->clazz = clazz; } mid = reinterpret_cast<jmethodID>(entry->memberId); clazz = entry->clazz; sig = &entry->sig; returnType = &entry->returnType; retType = entry->retType; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); clazz = env.FindClass(className); if (clazz != nullptr) { mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); return; } } else { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); return; } } clazz = mi.clazz; mid = mi.mid; sig = &mi.signature; returnType = &mi.returnType; retType = mi.retType; } if (!isStatic) { DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); } else { DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); } JsArgConverter argConverter(args, false, *sig, entry); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); throw NativeScriptException(err.msg); } auto isolate = Isolate::GetCurrent(); jweak callerJavaObject; jvalue* javaArgs = argConverter.ToArgs(); if (!isStatic) { callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); if(callerJavaObject == nullptr) { stringstream ss; ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; throw NativeScriptException(ss.str()); } } switch (retType) { case MethodReturnType::Void: { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); } else if (isSuper) { env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); } else { env.CallVoidMethodA(callerJavaObject, mid, javaArgs); } break; } case MethodReturnType::Boolean: { jboolean result; if (isStatic) { result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); break; } case MethodReturnType::Byte: { jbyte result; if (isStatic) { result = env.CallStaticByteMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Char: { jchar result; if (isStatic) { result = env.CallStaticCharMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); args.GetReturnValue().Set(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case MethodReturnType::Short: { jshort result; if (isStatic) { result = env.CallStaticShortMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Int: { jint result; if (isStatic) { result = env.CallStaticIntMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Long: { jlong result; if (isStatic) { result = env.CallStaticLongMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); } auto jsLong = ArgConverter::ConvertFromJavaLong(result); args.GetReturnValue().Set(jsLong); break; } case MethodReturnType::Float: { jfloat result; if (isStatic) { result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. break; } case MethodReturnType::Double: { jdouble result; if (isStatic) { result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::String: { jobject result = nullptr; bool exceptionOccurred; if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } if (result != nullptr) { auto objectResult = ArgConverter::jstringToV8String(static_cast<jstring>(result)); args.GetReturnValue().Set(objectResult); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } case MethodReturnType::Object: { jobject result = nullptr; bool exceptionOccurred; if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } if (result != nullptr) { auto isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Local<Value> objectResult; if (isString) { objectResult = ArgConverter::jstringToV8String((jstring)result); } else { jint javaObjectID = objectManager->GetOrCreateObjectId(result); objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, *returnType, result); } } args.GetReturnValue().Set(objectResult); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } default: { assert(false); break; } } static uint32_t adjustMemCount = 0; if ((++adjustMemCount % 2) == 0) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }
void NativeScriptRuntime::CallJavaMethod(const Handle<Object>& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); JEnv env; jclass clazz; jmethodID mid; string sig; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { entry->clazz = env.FindClass(className); if (entry->clazz == nullptr) { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); clazz = env.FindClass(callerClassName); if (clazz == nullptr) { DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } mid = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (mid == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(entry->clazz, methodName, entry->sig) : env.GetMethodID(entry->clazz, methodName, entry->sig); if (entry->memberId == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } } if (entry->clazz != nullptr) { clazz = entry->clazz; mid = reinterpret_cast<jmethodID>(entry->memberId); } sig = entry->sig; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); MethodCache::CacheMethodInfo mi; clazz = env.FindClass(className); if (clazz != nullptr) { mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); return; } } else { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); return; } } clazz = mi.clazz; mid = mi.mid; sig = mi.signature; } if (!isStatic) { DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); } else { DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); } JsArgConverter argConverter(args, false, sig); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); ExceptionUtil::GetInstance()->ThrowExceptionToJs(err.msg); return; } auto isolate = Isolate::GetCurrent(); jweak callerJavaObject; jvalue* javaArgs = argConverter.ToArgs(); if (!isStatic) { callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); if(callerJavaObject == nullptr) { stringstream ss; ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; string exceptionMessage = ss.str(); isolate->ThrowException(v8::Exception::ReferenceError(ConvertToV8String(exceptionMessage))); return; } } auto returnType = GetReturnType(sig); switch (returnType[0]) { case 'V': //void { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); } else if (isSuper) { env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); } else { env.CallVoidMethodA(callerJavaObject, mid, javaArgs); } break; } case 'Z': //bool { jboolean result; if (isStatic) { result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); break; } case 'B': //byte { jbyte result; if (isStatic) { result = env.CallStaticByteMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'C': //char { jchar result; if (isStatic) { result = env.CallStaticCharMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); args.GetReturnValue().Set(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case 'S': //short { jshort result; if (isStatic) { result = env.CallStaticShortMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'I': //int { jint result; if (isStatic) { result = env.CallStaticIntMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'J': //long { jlong result; if (isStatic) { result = env.CallStaticLongMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); } auto jsLong = ArgConverter::ConvertFromJavaLong(result); args.GetReturnValue().Set(jsLong); break; } case 'F': //float { jfloat result; if (isStatic) { result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. break; } case 'D': //double { jdouble result; if (isStatic) { result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case '[': case 'L': { bool isString = returnType == "Ljava/lang/String;"; jobject result = nullptr; bool exceptionOccurred; if (isString) { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { auto resultV8String = ArgConverter::jstringToV8String((jstring) result); args.GetReturnValue().Set(resultV8String); } } else { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { if (result != nullptr) { isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Handle<Value> objectResult; if (isString) { objectResult = ArgConverter::jstringToV8String((jstring)result); } else { jint javaObjectID = objectManager->GetOrCreateObjectId(result); objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, returnType, result); } } args.GetReturnValue().Set(objectResult); } else { args.GetReturnValue().Set(Null(isolate)); } } } if (!exceptionOccurred) { env.DeleteLocalRef(result); } break; } default: { // TODO: ASSERT_FAIL("Unknown return type"); break; } } if (!ExceptionUtil::GetInstance()->CheckForJavaException(env)) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }