diff --git a/test-app/runtime/src/main/cpp/JEnv.cpp b/test-app/runtime/src/main/cpp/JEnv.cpp index b0b477f78..345e70db6 100644 --- a/test-app/runtime/src/main/cpp/JEnv.cpp +++ b/test-app/runtime/src/main/cpp/JEnv.cpp @@ -723,16 +723,31 @@ jclass JEnv::FindClass(const string &className) { jclass global_class = CheckForClassInCache(className); if (global_class == nullptr) { + auto classIsMissing = CheckForClassMissingCache(className); + // class is missing. Set the same JNI error we had when we tried to find it the first time + if (classIsMissing != nullptr) { + m_env->Throw(classIsMissing); + return nullptr; + } jclass tmp = m_env->FindClass(className.c_str()); if (m_env->ExceptionCheck() == JNI_TRUE) { m_env->ExceptionClear(); - string cannonicalClassName = Util::ConvertFromJniToCanonicalName(className); - jstring s = m_env->NewStringUTF(cannonicalClassName.c_str()); - tmp = static_cast(m_env->CallStaticObjectMethod(RUNTIME_CLASS, - GET_CACHED_CLASS_METHOD_ID, s)); - - m_env->DeleteLocalRef(s); + string cannonicalClassName = Util::ConvertFromJniToCanonicalName(className); + jstring s = m_env->NewStringUTF(cannonicalClassName.c_str()); + tmp = static_cast(m_env->CallStaticObjectMethod(RUNTIME_CLASS, + GET_CACHED_CLASS_METHOD_ID, s)); + + m_env->DeleteLocalRef(s); + // we failed our static class check + // if we continue, we will crash (C++ level) + // so just return null and let the runtime deal with the NativeScriptException + if (m_env->ExceptionCheck() == JNI_TRUE) { + auto tmpException = m_env->ExceptionOccurred(); + m_env->ExceptionClear(); + m_env->Throw(InsertClassIntoMissingCache(className, tmpException)); + return nullptr; + } } global_class = InsertClassIntoCache(className, tmp); @@ -760,6 +775,25 @@ jclass JEnv::InsertClassIntoCache(const string &className, jclass &tmp) { return global_class; } +jthrowable JEnv::CheckForClassMissingCache(const string &className) { + jthrowable throwable = nullptr; + auto itFound = s_missingClasses.find(className); + + if (itFound != s_missingClasses.end()) { + throwable = itFound->second; + } + + return throwable; +} + +jthrowable JEnv::InsertClassIntoMissingCache(const string &className,const jthrowable &tmp) { + auto throwable = reinterpret_cast(m_env->NewGlobalRef(tmp)); + s_missingClasses.insert(make_pair(className, throwable)); + m_env->DeleteLocalRef(tmp); + + return throwable; +} + jobject JEnv::NewDirectByteBuffer(void *address, jlong capacity) { jobject jo = m_env->NewDirectByteBuffer(address, capacity); CheckForJavaException(); @@ -822,6 +856,7 @@ void JEnv::CheckForJavaException() { JavaVM *JEnv::s_jvm = nullptr; map JEnv::s_classCache; +map JEnv::s_missingClasses; jclass JEnv::RUNTIME_CLASS = nullptr; jmethodID JEnv::GET_CACHED_CLASS_METHOD_ID = nullptr; diff --git a/test-app/runtime/src/main/cpp/JEnv.h b/test-app/runtime/src/main/cpp/JEnv.h index 8f1f483df..a85fcc637 100644 --- a/test-app/runtime/src/main/cpp/JEnv.h +++ b/test-app/runtime/src/main/cpp/JEnv.h @@ -191,6 +191,16 @@ class JEnv { */ jclass InsertClassIntoCache(const std::string& className, jclass& tmp); + + /* + * The "CheckForClassMissing" will check if a class has been checked and it was missing, if it is, it will return the original throwable + * this is useful for rethrowing exceptions if they were caught in the previous attempt of loading it. + * if it is not: it will return "nullptr". + */ + jthrowable CheckForClassMissingCache(const std::string& className); + + jthrowable InsertClassIntoMissingCache(const std::string& className, const jthrowable& tmp); + jobject NewDirectByteBuffer(void* address, jlong capacity); void* GetDirectBufferAddress(jobject buf); jlong GetDirectBufferCapacity(jobject buf); @@ -333,6 +343,7 @@ class JEnv { static jmethodID GET_CACHED_CLASS_METHOD_ID; static std::map s_classCache; + static std::map s_missingClasses; }; } diff --git a/test-app/runtime/src/main/cpp/MetadataNode.cpp b/test-app/runtime/src/main/cpp/MetadataNode.cpp index 9c5739eac..da5be5afd 100644 --- a/test-app/runtime/src/main/cpp/MetadataNode.cpp +++ b/test-app/runtime/src/main/cpp/MetadataNode.cpp @@ -884,6 +884,10 @@ void MetadataNode::SetInnerTypes(Isolate* isolate, Local& ctorFunction // The call to GetConstructorFunctionTemplate bootstraps the ctor function for the childNode auto innerTypeCtorFuncTemplate = childNode->GetConstructorFunctionTemplate(isolate, curChild); + if(innerTypeCtorFuncTemplate.IsEmpty()) { + // this class does not exist, so just ignore it + continue; + } auto innerTypeCtorFunc = Local::New(isolate, *GetOrCreateInternal(curChild)->GetPersistentConstructorFunction(isolate)); auto innerTypeName = ArgConverter::ConvertToV8String(isolate, curChild->name); ctorFunction->Set(context, innerTypeName, innerTypeCtorFunc); @@ -917,6 +921,25 @@ Local MetadataNode::GetConstructorFunctionTemplate(Isolate* is // auto node = GetOrCreateInternal(treeNode); + + + JEnv env; + // if we already have an exception (which will be rethrown later) + // then we don't want to ignore the next exception + bool ignoreFindClassException = env.ExceptionCheck() == JNI_FALSE; + auto currentClass = env.FindClass(node->m_name); + if (ignoreFindClassException && env.ExceptionCheck()) { + env.ExceptionClear(); + // JNI found an exception looking up this class + // but we don't care, because this means this class doesn't exist + // like when you try to get a class that only exists in a higher API level + ctorFuncTemplate.Clear(); + auto pft = new Persistent(isolate, ctorFuncTemplate); + CtorCacheData ctorCacheItem(pft, instanceMethodsCallbackData); + cache->CtorFuncCache.insert(make_pair(treeNode, ctorCacheItem)); + return ctorFuncTemplate; + } + auto ctorCallbackData = External::New(isolate, node); auto isInterface = s_metadataReader.IsNodeTypeInterface(treeNode->type); auto funcCallback = isInterface ? InterfaceConstructorCallback : ClassConstructorCallback; @@ -926,8 +949,6 @@ Local MetadataNode::GetConstructorFunctionTemplate(Isolate* is Local baseCtorFunc; std::vector baseInstanceMethodsCallbackData; auto tmpTreeNode = treeNode; - JEnv env; - auto currentClass = env.FindClass(node->m_name); std::vector skippedBaseTypes; while (true) { auto baseTreeNode = s_metadataReader.GetBaseClassNode(tmpTreeNode);