diff --git a/NativeScript/ffi/jni/jsi/callbackhandlers/CallbackHandlers.cpp b/NativeScript/ffi/jni/jsi/callbackhandlers/CallbackHandlers.cpp index bda4ac27..c703e25b 100644 --- a/NativeScript/ffi/jni/jsi/callbackhandlers/CallbackHandlers.cpp +++ b/NativeScript/ffi/jni/jsi/callbackhandlers/CallbackHandlers.cpp @@ -133,6 +133,27 @@ void CallbackHandlers::Init(JsRuntime &rt) { MethodCache::Init(); } +static MetadataNode *ResolveDeclaredReturnNode(JEnv &jEnv, MetadataEntry *entry, jobject result) { + if (entry == nullptr) return nullptr; + if (!entry->returnClassResolved) { + entry->returnClassResolved = true; + const auto &returnType = entry->getReturnType(); + if (returnType.size() > 2 && returnType[0] == 'L') { + const std::string declaredName = returnType.substr(1, returnType.size() - 2); + jclass declaredClass = jEnv.FindClass(declaredName); + if (declaredClass != nullptr) { + entry->returnClass = declaredClass; + entry->returnNode = MetadataNode::GetOrCreate(declaredName); + } else { + jEnv.ExceptionClear(); + } + } + } + if (entry->returnClass == nullptr) return nullptr; + JniLocalRef runtimeClass(jEnv.GetObjectClass(result)); + return jEnv.isSameObject(runtimeClass, entry->returnClass) ? entry->returnNode : nullptr; +} + JsValue CallbackHandlers::CallJavaMethod(JsRuntime &rt, const JsValue &caller, const string &className, const string &methodName, MetadataEntry *entry, bool isFromInterface, bool isStatic, bool isConstructorCall, @@ -496,8 +517,10 @@ JsValue CallbackHandlers::CallJavaMethod(JsRuntime &rt, const JsValue &caller, c returnValue = objectManager->GetJsObjectByJavaObject(javaObjectID); if (js_util::is_null_or_undefined(returnValue)) { - returnValue = objectManager->CreateJSWrapper(javaObjectID, *returnType, - result); + MetadataNode *returnNode = ResolveDeclaredReturnNode(jEnv, entry, result); + returnValue = returnNode != nullptr + ? objectManager->CreateJSWrapper(javaObjectID, returnNode, entry->returnClass) + : objectManager->CreateJSWrapper(javaObjectID, *returnType, result); } } diff --git a/NativeScript/ffi/jni/jsi/metadata/MetadataEntry.h b/NativeScript/ffi/jni/jsi/metadata/MetadataEntry.h index 1cb79b07..4fa6da20 100644 --- a/NativeScript/ffi/jni/jsi/metadata/MetadataEntry.h +++ b/NativeScript/ffi/jni/jsi/metadata/MetadataEntry.h @@ -7,6 +7,8 @@ #include "MetadataMethodInfo.h" #include "MetadataFieldInfo.h" +class MetadataNode; + namespace tns { enum class NodeType { Package, @@ -49,6 +51,9 @@ namespace tns { memberId = other.memberId; clazz = other.clazz; parsedSig = other.parsedSig; + returnClass = other.returnClass; + returnNode = other.returnNode; + returnClassResolved = other.returnClassResolved; mi = other.mi; fi = other.fi; sfi = other.sfi; @@ -91,6 +96,9 @@ namespace tns { void *memberId; jclass clazz; std::vector parsedSig; + jclass returnClass = nullptr; + ::MetadataNode *returnNode = nullptr; + bool returnClassResolved = false; MethodInfo mi; FieldInfo *fi; diff --git a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp index 08e00096..57029efe 100644 --- a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp +++ b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp @@ -631,20 +631,24 @@ JsValue ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const std::strin auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; auto node = MetadataNode::GetOrCreate(className); + // Reuse the class we already resolved via GetObjectClass on the instance + // path instead of re-resolving it with a JNI FindClass. The class is only + // stored on JSInstanceInfo::ObjectClazz, which nothing on this path reads, + // so a fresh FindClass is pure overhead; only fall back to it for the + // typeName-only overload where no instance class was available. + jclass linkClazz = clazz; + if (linkClazz == nullptr) { + JEnv jenv; + linkClazz = jenv.FindClass(className); + } + return CreateJSWrapper(javaObjectID, node, linkClazz); +} + +JsValue ObjectManager::CreateJSWrapper(jint javaObjectID, MetadataNode *node, jclass clazz) { JsValue proxy = js_util::undefined(); JsValue jsWrapper = node->CreateJSWrapper(*m_rt, this); if (jsWrapper.isObject()) { - // Reuse the class we already resolved via GetObjectClass on the instance - // path instead of re-resolving it with a JNI FindClass. The class is only - // stored on JSInstanceInfo::ObjectClazz, which nothing on this path reads, - // so a fresh FindClass is pure overhead; only fall back to it for the - // typeName-only overload where no instance class was available. - jclass linkClazz = clazz; - if (linkClazz == nullptr) { - JEnv jenv; - linkClazz = jenv.FindClass(className); - } - Link(jsWrapper, javaObjectID, linkClazz, node); + Link(jsWrapper, javaObjectID, clazz, node); if (node->isArray()) { jsWrapper.asObject(*m_rt).setProperty(*m_rt, "__is__javaArray", true); } diff --git a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.h b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.h index c5972075..1fc5b21f 100644 --- a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.h +++ b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.h @@ -46,6 +46,8 @@ namespace tns { JsValue CreateJSWrapper(jint javaObjectID, const std::string &typeName, jobject instance); + JsValue CreateJSWrapper(jint javaObjectID, MetadataNode *node, jclass clazz); + JsValue GetOrCreateProxy(jint javaObjectID, const JsValue &instance); JsValue GetOrCreateProxyWeak(jint javaObjectID, const JsValue &instance); diff --git a/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp index 3dff0ff5..5efd897f 100644 --- a/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp +++ b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp @@ -119,6 +119,27 @@ void CallbackHandlers::Init(napi_env env) { MethodCache::Init(); } +static MetadataNode *ResolveDeclaredReturnNode(JEnv &jEnv, MetadataEntry *entry, jobject result) { + if (entry == nullptr) return nullptr; + if (!entry->returnClassResolved) { + entry->returnClassResolved = true; + const auto &returnType = entry->getReturnType(); + if (returnType.size() > 2 && returnType[0] == 'L') { + const std::string declaredName = returnType.substr(1, returnType.size() - 2); + jclass declaredClass = jEnv.FindClass(declaredName); + if (declaredClass != nullptr) { + entry->returnClass = declaredClass; + entry->returnNode = MetadataNode::GetOrCreate(declaredName); + } else { + jEnv.ExceptionClear(); + } + } + } + if (entry->returnClass == nullptr) return nullptr; + JniLocalRef runtimeClass(jEnv.GetObjectClass(result)); + return jEnv.isSameObject(runtimeClass, entry->returnClass) ? entry->returnNode : nullptr; +} + napi_value CallbackHandlers::CallJavaMethod(napi_env env, napi_value caller, const string &className, const string &methodName, MetadataEntry *entry, bool isFromInterface, bool isStatic, napi_callback_info info, size_t argc, napi_value* argv, @@ -500,8 +521,10 @@ napi_value CallbackHandlers::CallJavaMethod(napi_env env, napi_value caller, con returnValue = objectManager->GetJsObjectByJavaObject(javaObjectID); if (napi_util::is_null_or_undefined(env, returnValue)) { - returnValue = objectManager->CreateJSWrapper(javaObjectID, *returnType, - result); + MetadataNode *returnNode = ResolveDeclaredReturnNode(jEnv, entry, result); + returnValue = returnNode != nullptr + ? objectManager->CreateJSWrapper(javaObjectID, returnNode, entry->returnClass) + : objectManager->CreateJSWrapper(javaObjectID, *returnType, result); } } diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h index 1cb79b07..4fa6da20 100644 --- a/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h +++ b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h @@ -7,6 +7,8 @@ #include "MetadataMethodInfo.h" #include "MetadataFieldInfo.h" +class MetadataNode; + namespace tns { enum class NodeType { Package, @@ -49,6 +51,9 @@ namespace tns { memberId = other.memberId; clazz = other.clazz; parsedSig = other.parsedSig; + returnClass = other.returnClass; + returnNode = other.returnNode; + returnClassResolved = other.returnClassResolved; mi = other.mi; fi = other.fi; sfi = other.sfi; @@ -91,6 +96,9 @@ namespace tns { void *memberId; jclass clazz; std::vector parsedSig; + jclass returnClass = nullptr; + ::MetadataNode *returnNode = nullptr; + bool returnClassResolved = false; MethodInfo mi; FieldInfo *fi; diff --git a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp index e07f68f3..92ebe21d 100644 --- a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp +++ b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp @@ -818,24 +818,28 @@ ObjectManager::CreateJSWrapper(jint javaObjectID, const std::string &typeName, j napi_value ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const std::string &typeName, jclass clazz) { - napi_status status; auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; auto node = MetadataNode::GetOrCreate(className); + // Reuse the class we already resolved via GetObjectClass on the instance + // path instead of re-resolving it with a JNI FindClass. The class is only + // stored on JSInstanceInfo::ObjectClazz, which nothing on this path reads, + // so a fresh FindClass is pure overhead; only fall back to it for the + // typeName-only overload where no instance class was available. + jclass linkClazz = clazz; + if (linkClazz == nullptr) { + JEnv jenv; + linkClazz = jenv.FindClass(className); + } + return CreateJSWrapper(javaObjectID, node, linkClazz); +} + +napi_value ObjectManager::CreateJSWrapper(jint javaObjectID, MetadataNode *node, jclass clazz) { + napi_status status; napi_value proxy = nullptr; napi_value jsWrapper = node->CreateJSWrapper(m_env, this); if (jsWrapper != nullptr) { - // Reuse the class we already resolved via GetObjectClass on the instance - // path instead of re-resolving it with a JNI FindClass. The class is only - // stored on JSInstanceInfo::ObjectClazz, which nothing on this path reads, - // so a fresh FindClass is pure overhead; only fall back to it for the - // typeName-only overload where no instance class was available. - jclass linkClazz = clazz; - if (linkClazz == nullptr) { - JEnv jenv; - linkClazz = jenv.FindClass(className); - } - Link(jsWrapper, javaObjectID, linkClazz, node); + Link(jsWrapper, javaObjectID, clazz, node); if (node->isArray()) { NAPI_GUARD(napi_set_named_property(m_env, jsWrapper, "__is__javaArray", napi_util::get_true(m_env))) {} diff --git a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h index 942e86b4..5aaaf947 100644 --- a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h +++ b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h @@ -46,6 +46,8 @@ namespace tns { napi_value CreateJSWrapper(jint javaObjectID, const std::string &typeName, jobject instance); + napi_value CreateJSWrapper(jint javaObjectID, MetadataNode *node, jclass clazz); + napi_value GetOrCreateProxy(jint javaObjectID, napi_value instance); napi_value GetOrCreateProxyWeak(jint javaObjectID, napi_value instance);