diff --git a/NativeScript/ffi/jni/jsi/jni/LRUCache.h b/NativeScript/ffi/jni/jsi/jni/LRUCache.h index 7b829c3dd..afceeb132 100644 --- a/NativeScript/ffi/jni/jsi/jni/LRUCache.h +++ b/NativeScript/ffi/jni/jsi/jni/LRUCache.h @@ -100,6 +100,16 @@ class LRUCache { } } + void clear() { + if (m_evictCallback != nullptr) { + for (auto& entry : m_key_to_value) { + m_evictCallback(entry.second.first, m_state); + } + } + m_key_to_value.clear(); + m_key_tracker.clear(); + } + void update(const key_type& key, const value_type& value) { jweak ref = m_loadCallback(key, m_state); insert(key, ref); diff --git a/NativeScript/ffi/jni/jsi/metadata/MetadataNode.cpp b/NativeScript/ffi/jni/jsi/metadata/MetadataNode.cpp index 6187a585d..18c937afe 100644 --- a/NativeScript/ffi/jni/jsi/metadata/MetadataNode.cpp +++ b/NativeScript/ffi/jni/jsi/metadata/MetadataNode.cpp @@ -1307,6 +1307,12 @@ std::vector MetadataNode::SetClassMembersFro callbackData->candidates.push_back(std::move(entry)); callbackData->objectManager = objectManager; } + for (const auto &extension: collectedExtensionMethods) { + if (std::find(instanceMethodData.begin(), instanceMethodData.end(), extension.second) == + instanceMethodData.end()) { + cache->methodCallbackData.push_back(extension.second); + } + } auto instanceFieldCount = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); for (auto i = 0; i < instanceFieldCount; i++) { @@ -1392,6 +1398,7 @@ std::vector MetadataNode::SetClassMembersFro auto &methodName = entry.getName(); if (methodName != lastMethodName) { callbackData = new MethodCallbackData(this); + cache->methodCallbackData.push_back(callbackData); auto method = JsFunction::createFromHostFunction( rt, engine::PropNameID::forAscii(rt, methodName), 0, [callbackData](JsRuntime &rt, const JsValue &thisVal, const JsValue *args, @@ -2556,6 +2563,7 @@ void MetadataNode::SetMissingBaseMethods( if (callbackData == nullptr) { callbackData = new MethodCallbackData(this); + GetMetadataNodeCache(rt)->methodCallbackData.push_back(callbackData); auto proto = constructor.getPropertyAsObject(rt, "prototype"); auto method = JsFunction::createFromHostFunction( rt, engine::PropNameID::forAscii(rt, methodName), 0, @@ -2613,6 +2621,9 @@ void MetadataNode::onDisposeRuntime(JsRuntime &rt) { for (const auto &entry: it->extendedClassCallbackData) { delete entry; } + for (const auto &entry: it->methodCallbackData) { + delete entry; + } } s_metadata_node_cache.Remove(key); delete it; diff --git a/NativeScript/ffi/jni/jsi/metadata/MetadataNode.h b/NativeScript/ffi/jni/jsi/metadata/MetadataNode.h index 2c06df48a..265783b4a 100644 --- a/NativeScript/ffi/jni/jsi/metadata/MetadataNode.h +++ b/NativeScript/ffi/jni/jsi/metadata/MetadataNode.h @@ -373,6 +373,9 @@ class MetadataNode { std::vector fieldCallbackData; std::vector propertyCallbackData; std::vector extendedClassCallbackData; + // Method callback data no CtorFuncCache entry owns: static methods, + // extension-only methods and methods copied from a skipped base class. + std::vector methodCallbackData; }; static bool s_profilerEnabled; diff --git a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp index 394a45446..08e000964 100644 --- a/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp +++ b/NativeScript/ffi/jni/jsi/objectmanager/ObjectManager.cpp @@ -85,6 +85,10 @@ void ObjectManager::OnDisposeRuntime() { m_idToObject.clear(); m_jsObjectCtor = JsFunction(); + // Its destructor leaves the weak global refs alive, and the process-wide + // weak reference table is finite. + m_cache.clear(); + // The host-object proxies are owned by the *engine*, and each holds an owned // handle to the instance it wraps. Nothing above reaches them: they are only // destroyed when the engine tears its heap down, at which point diff --git a/NativeScript/ffi/jni/napi/jni/LRUCache.h b/NativeScript/ffi/jni/napi/jni/LRUCache.h index 7b829c3dd..afceeb132 100644 --- a/NativeScript/ffi/jni/napi/jni/LRUCache.h +++ b/NativeScript/ffi/jni/napi/jni/LRUCache.h @@ -100,6 +100,16 @@ class LRUCache { } } + void clear() { + if (m_evictCallback != nullptr) { + for (auto& entry : m_key_to_value) { + m_evictCallback(entry.second.first, m_state); + } + } + m_key_to_value.clear(); + m_key_tracker.clear(); + } + void update(const key_type& key, const value_type& value) { jweak ref = m_loadCallback(key, m_state); insert(key, ref); diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp index 6e9e48219..582386eb5 100644 --- a/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp +++ b/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp @@ -1584,6 +1584,12 @@ std::vector MetadataNode::SetClassMembersFro callbackData->candidates.push_back(std::move(entry)); callbackData->objectManager = objectManager; } + for (const auto &extension: collectedExtensionMethods) { + if (std::find(instanceMethodData.begin(), instanceMethodData.end(), extension.second) == + instanceMethodData.end()) { + GetMetadataNodeCache(env)->methodCallbackData.push_back(extension.second); + } + } auto instanceFieldCount = *reinterpret_cast(curPtr); curPtr += sizeof(uint16_t); for (auto i = 0; i < instanceFieldCount; i++) { @@ -1657,6 +1663,7 @@ std::vector MetadataNode::SetClassMembersFro auto &methodName = entry.getName(); if (methodName != lastMethodName) { callbackData = new MethodCallbackData(this); + GetMetadataNodeCache(env)->methodCallbackData.push_back(callbackData); napi_value method; NAPI_GUARD(napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, callbackData, &method)) {} @@ -3054,6 +3061,7 @@ void MetadataNode::SetMissingBaseMethods( if (callbackData == nullptr) { callbackData = new MethodCallbackData(this); + GetMetadataNodeCache(env)->methodCallbackData.push_back(callbackData); napi_value proto = napi_util::get_prototype(env, constructor); napi_value method; NAPI_GUARD(napi_create_function(env, methodName.c_str(), NAPI_AUTO_LENGTH, MethodCallback, @@ -3105,6 +3113,9 @@ void MetadataNode::onDisposeEnv(napi_env env) { for (const auto &entry: it->fieldCallbackData) { delete entry; } + for (const auto &entry: it->methodCallbackData) { + delete entry; + } } s_metadata_node_cache.Remove(env); delete it; diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataNode.h b/NativeScript/ffi/jni/napi/metadata/MetadataNode.h index b6c699436..fabe0f507 100644 --- a/NativeScript/ffi/jni/napi/metadata/MetadataNode.h +++ b/NativeScript/ffi/jni/napi/metadata/MetadataNode.h @@ -360,6 +360,9 @@ class MetadataNode { robin_hood::unordered_map CtorFuncCache; robin_hood::unordered_map ExtendedCtorFuncCache; std::vector fieldCallbackData; + // Method callback data no CtorFuncCache entry owns: static methods, + // extension-only methods and methods copied from a skipped base class. + std::vector methodCallbackData; }; static bool s_profilerEnabled; diff --git a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp index a5c67014a..7bafbfe41 100644 --- a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp +++ b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp @@ -92,6 +92,10 @@ void ObjectManager::OnDisposeEnv() { NAPI_GUARD(napi_delete_reference(m_env, entry.second)) {} } m_idToObject.clear(); + + // Its destructor leaves the weak global refs alive, and the process-wide + // weak reference table is finite. + m_cache.clear(); } napi_value ObjectManager::GetOrCreateProxyWeak(jint javaObjectID, napi_value instance) { diff --git a/NativeScript/runtime/android/jsi/Runtime.cpp b/NativeScript/runtime/android/jsi/Runtime.cpp index 7991157d2..8290b1406 100644 --- a/NativeScript/runtime/android/jsi/Runtime.cpp +++ b/NativeScript/runtime/android/jsi/Runtime.cpp @@ -547,6 +547,8 @@ Runtime::~Runtime() { close(m_mainLooper_fd[1]); } } + + JEnv().DeleteGlobalRef(m_runtime); } std::string Runtime::ReadFileText(const std::string &filePath) { diff --git a/NativeScript/runtime/android/napi/Runtime.cpp b/NativeScript/runtime/android/napi/Runtime.cpp index 49fc0455f..c6f0ce75e 100644 --- a/NativeScript/runtime/android/napi/Runtime.cpp +++ b/NativeScript/runtime/android/napi/Runtime.cpp @@ -486,6 +486,8 @@ Runtime::~Runtime() { close(m_mainLooper_fd[1]); } } + + JEnv().DeleteGlobalRef(m_runtime); } std::string Runtime::ReadFileText(const std::string &filePath) {