Skip to content

Commit c238166

Browse files
authored
fix: handle missing child classes when querying native classes (#1718)
1 parent bdd0313 commit c238166

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

test-app/runtime/src/main/cpp/JEnv.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,16 +723,31 @@ jclass JEnv::FindClass(const string &className) {
723723
jclass global_class = CheckForClassInCache(className);
724724

725725
if (global_class == nullptr) {
726+
auto classIsMissing = CheckForClassMissingCache(className);
727+
// class is missing. Set the same JNI error we had when we tried to find it the first time
728+
if (classIsMissing != nullptr) {
729+
m_env->Throw(classIsMissing);
730+
return nullptr;
731+
}
726732
jclass tmp = m_env->FindClass(className.c_str());
727733

728734
if (m_env->ExceptionCheck() == JNI_TRUE) {
729735
m_env->ExceptionClear();
730-
string cannonicalClassName = Util::ConvertFromJniToCanonicalName(className);
731-
jstring s = m_env->NewStringUTF(cannonicalClassName.c_str());
732-
tmp = static_cast<jclass>(m_env->CallStaticObjectMethod(RUNTIME_CLASS,
733-
GET_CACHED_CLASS_METHOD_ID, s));
734-
735-
m_env->DeleteLocalRef(s);
736+
string cannonicalClassName = Util::ConvertFromJniToCanonicalName(className);
737+
jstring s = m_env->NewStringUTF(cannonicalClassName.c_str());
738+
tmp = static_cast<jclass>(m_env->CallStaticObjectMethod(RUNTIME_CLASS,
739+
GET_CACHED_CLASS_METHOD_ID, s));
740+
741+
m_env->DeleteLocalRef(s);
742+
// we failed our static class check
743+
// if we continue, we will crash (C++ level)
744+
// so just return null and let the runtime deal with the NativeScriptException
745+
if (m_env->ExceptionCheck() == JNI_TRUE) {
746+
auto tmpException = m_env->ExceptionOccurred();
747+
m_env->ExceptionClear();
748+
m_env->Throw(InsertClassIntoMissingCache(className, tmpException));
749+
return nullptr;
750+
}
736751
}
737752

738753
global_class = InsertClassIntoCache(className, tmp);
@@ -760,6 +775,25 @@ jclass JEnv::InsertClassIntoCache(const string &className, jclass &tmp) {
760775
return global_class;
761776
}
762777

778+
jthrowable JEnv::CheckForClassMissingCache(const string &className) {
779+
jthrowable throwable = nullptr;
780+
auto itFound = s_missingClasses.find(className);
781+
782+
if (itFound != s_missingClasses.end()) {
783+
throwable = itFound->second;
784+
}
785+
786+
return throwable;
787+
}
788+
789+
jthrowable JEnv::InsertClassIntoMissingCache(const string &className,const jthrowable &tmp) {
790+
auto throwable = reinterpret_cast<jthrowable>(m_env->NewGlobalRef(tmp));
791+
s_missingClasses.insert(make_pair(className, throwable));
792+
m_env->DeleteLocalRef(tmp);
793+
794+
return throwable;
795+
}
796+
763797
jobject JEnv::NewDirectByteBuffer(void *address, jlong capacity) {
764798
jobject jo = m_env->NewDirectByteBuffer(address, capacity);
765799
CheckForJavaException();
@@ -822,6 +856,7 @@ void JEnv::CheckForJavaException() {
822856

823857
JavaVM *JEnv::s_jvm = nullptr;
824858
map<string, jclass> JEnv::s_classCache;
859+
map<string, jthrowable> JEnv::s_missingClasses;
825860
jclass JEnv::RUNTIME_CLASS = nullptr;
826861
jmethodID JEnv::GET_CACHED_CLASS_METHOD_ID = nullptr;
827862

test-app/runtime/src/main/cpp/JEnv.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ class JEnv {
191191
*/
192192
jclass InsertClassIntoCache(const std::string& className, jclass& tmp);
193193

194+
195+
/*
196+
* The "CheckForClassMissing" will check if a class has been checked and it was missing, if it is, it will return the original throwable
197+
* this is useful for rethrowing exceptions if they were caught in the previous attempt of loading it.
198+
* if it is not: it will return "nullptr".
199+
*/
200+
jthrowable CheckForClassMissingCache(const std::string& className);
201+
202+
jthrowable InsertClassIntoMissingCache(const std::string& className, const jthrowable& tmp);
203+
194204
jobject NewDirectByteBuffer(void* address, jlong capacity);
195205
void* GetDirectBufferAddress(jobject buf);
196206
jlong GetDirectBufferCapacity(jobject buf);
@@ -333,6 +343,7 @@ class JEnv {
333343
static jmethodID GET_CACHED_CLASS_METHOD_ID;
334344

335345
static std::map<std::string, jclass> s_classCache;
346+
static std::map<std::string, jthrowable> s_missingClasses;
336347
};
337348
}
338349

test-app/runtime/src/main/cpp/MetadataNode.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,10 @@ void MetadataNode::SetInnerTypes(Isolate* isolate, Local<Function>& ctorFunction
884884

885885
// The call to GetConstructorFunctionTemplate bootstraps the ctor function for the childNode
886886
auto innerTypeCtorFuncTemplate = childNode->GetConstructorFunctionTemplate(isolate, curChild);
887+
if(innerTypeCtorFuncTemplate.IsEmpty()) {
888+
// this class does not exist, so just ignore it
889+
continue;
890+
}
887891
auto innerTypeCtorFunc = Local<Function>::New(isolate, *GetOrCreateInternal(curChild)->GetPersistentConstructorFunction(isolate));
888892
auto innerTypeName = ArgConverter::ConvertToV8String(isolate, curChild->name);
889893
ctorFunction->Set(context, innerTypeName, innerTypeCtorFunc);
@@ -917,6 +921,25 @@ Local<FunctionTemplate> MetadataNode::GetConstructorFunctionTemplate(Isolate* is
917921
//
918922

919923
auto node = GetOrCreateInternal(treeNode);
924+
925+
926+
JEnv env;
927+
// if we already have an exception (which will be rethrown later)
928+
// then we don't want to ignore the next exception
929+
bool ignoreFindClassException = env.ExceptionCheck() == JNI_FALSE;
930+
auto currentClass = env.FindClass(node->m_name);
931+
if (ignoreFindClassException && env.ExceptionCheck()) {
932+
env.ExceptionClear();
933+
// JNI found an exception looking up this class
934+
// but we don't care, because this means this class doesn't exist
935+
// like when you try to get a class that only exists in a higher API level
936+
ctorFuncTemplate.Clear();
937+
auto pft = new Persistent<FunctionTemplate>(isolate, ctorFuncTemplate);
938+
CtorCacheData ctorCacheItem(pft, instanceMethodsCallbackData);
939+
cache->CtorFuncCache.insert(make_pair(treeNode, ctorCacheItem));
940+
return ctorFuncTemplate;
941+
}
942+
920943
auto ctorCallbackData = External::New(isolate, node);
921944
auto isInterface = s_metadataReader.IsNodeTypeInterface(treeNode->type);
922945
auto funcCallback = isInterface ? InterfaceConstructorCallback : ClassConstructorCallback;
@@ -926,8 +949,6 @@ Local<FunctionTemplate> MetadataNode::GetConstructorFunctionTemplate(Isolate* is
926949
Local<Function> baseCtorFunc;
927950
std::vector<MethodCallbackData*> baseInstanceMethodsCallbackData;
928951
auto tmpTreeNode = treeNode;
929-
JEnv env;
930-
auto currentClass = env.FindClass(node->m_name);
931952
std::vector<MetadataTreeNode*> skippedBaseTypes;
932953
while (true) {
933954
auto baseTreeNode = s_metadataReader.GetBaseClassNode(tmpTreeNode);

0 commit comments

Comments
 (0)