Clear all held jsi::Functions when jsi::Runtime is deleted

Summary:
## Description
You're not supposed to hold on to JSI objects (ex: `jsi::Function`) past the point where their `jsi::Runtime` is deleted. Otherwise, we get a dangling pointer crash, like this: T60262810! Historically, this cleanup problem has always been really tricky to get right. With this diff, I hope to fix that problem once and for all by deleting all `jsi::Function`s when we delete the global `__turboModuleProxy` function.

## Current Setup
- The TurboModules infra uses weak references to `CallbackWrapper`s to hold on to the `jsi::Function`s passed from JS to ObjC.
- The LongLivedObjectCollection holds on to strong references to `CallbackWrapper`s. This ensures that the `jsi::Function`s aren't deleted prematurely. This also means that we can use `LongLivedObjectCollection` to delete all `CallbackWrappers`.
- `TurboModuleBinding` is the abstraction we use to install the global `__turboModuleProxy` function. It is owned by `TurboModuleManager`, and `TurboModuleManager` uses it to clear all references to `jsi::Function`s, when we delete all NativeModules.

## Solution
1. Transfer ownership of `TurboModuleBinding` from `TurboModuleManager` to the `__turboModuleProxy` function.
2. Clear the `LongLivedObjectCollection` when `TurboModuleBinding` is deleted.

Changelog:
[iOS][Fixed] - Clear all held jsi::Functions when jsi::Runtime is deleted

Reviewed By: JoshuaGross

Differential Revision: D19565499

fbshipit-source-id: e3510ea04e72f6bda363a8fc3ee2be60303b70a6
This commit is contained in:
Ramanpreet Nara
2020-01-30 15:15:09 -08:00
committed by Facebook Github Bot
parent 7001dc3fe0
commit 9ae95582e7
5 changed files with 86 additions and 108 deletions
@@ -65,65 +65,62 @@ void TurboModuleManager::installJSIBindings() {
TurboModuleBinding::install(
*runtime_,
std::make_shared<TurboModuleBinding>(
[turboModuleCache_ =
std::weak_ptr<TurboModuleCache>(turboModuleCache_),
jsCallInvoker_ = std::weak_ptr<CallInvoker>(jsCallInvoker_),
nativeCallInvoker_ = std::weak_ptr<CallInvoker>(nativeCallInvoker_),
delegate_ = jni::make_weak(delegate_),
javaPart_ = jni::make_weak(javaPart_)](
const std::string &name) -> std::shared_ptr<TurboModule> {
auto turboModuleCache = turboModuleCache_.lock();
auto jsCallInvoker = jsCallInvoker_.lock();
auto nativeCallInvoker = nativeCallInvoker_.lock();
auto delegate = delegate_.lockLocal();
auto javaPart = javaPart_.lockLocal();
[turboModuleCache_ = std::weak_ptr<TurboModuleCache>(turboModuleCache_),
jsCallInvoker_ = std::weak_ptr<CallInvoker>(jsCallInvoker_),
nativeCallInvoker_ = std::weak_ptr<CallInvoker>(nativeCallInvoker_),
delegate_ = jni::make_weak(delegate_),
javaPart_ = jni::make_weak(javaPart_)](
const std::string &name) -> std::shared_ptr<TurboModule> {
auto turboModuleCache = turboModuleCache_.lock();
auto jsCallInvoker = jsCallInvoker_.lock();
auto nativeCallInvoker = nativeCallInvoker_.lock();
auto delegate = delegate_.lockLocal();
auto javaPart = javaPart_.lockLocal();
if (!turboModuleCache || !jsCallInvoker || !nativeCallInvoker ||
!delegate || !javaPart) {
return nullptr;
}
if (!turboModuleCache || !jsCallInvoker || !nativeCallInvoker ||
!delegate || !javaPart) {
return nullptr;
}
auto turboModuleLookup = turboModuleCache->find(name);
if (turboModuleLookup != turboModuleCache->end()) {
return turboModuleLookup->second;
}
auto turboModuleLookup = turboModuleCache->find(name);
if (turboModuleLookup != turboModuleCache->end()) {
return turboModuleLookup->second;
}
auto cxxModule =
delegate->cthis()->getTurboModule(name, jsCallInvoker);
if (cxxModule) {
turboModuleCache->insert({name, cxxModule});
return cxxModule;
}
auto cxxModule = delegate->cthis()->getTurboModule(name, jsCallInvoker);
if (cxxModule) {
turboModuleCache->insert({name, cxxModule});
return cxxModule;
}
static auto getLegacyCxxModule =
delegate->getClass()
->getMethod<jni::alias_ref<CxxModuleWrapper::javaobject>(
const std::string &)>("getLegacyCxxModule");
auto legacyCxxModule = getLegacyCxxModule(delegate.get(), name);
static auto getLegacyCxxModule =
delegate->getClass()
->getMethod<jni::alias_ref<CxxModuleWrapper::javaobject>(
const std::string &)>("getLegacyCxxModule");
auto legacyCxxModule = getLegacyCxxModule(delegate.get(), name);
if (legacyCxxModule) {
auto turboModule = std::make_shared<react::TurboCxxModule>(
legacyCxxModule->cthis()->getModule(), jsCallInvoker);
turboModuleCache->insert({name, turboModule});
return turboModule;
}
if (legacyCxxModule) {
auto turboModule = std::make_shared<react::TurboCxxModule>(
legacyCxxModule->cthis()->getModule(), jsCallInvoker);
turboModuleCache->insert({name, turboModule});
return turboModule;
}
static auto getJavaModule =
javaPart->getClass()
->getMethod<jni::alias_ref<JTurboModule>(
const std::string &)>("getJavaModule");
auto moduleInstance = getJavaModule(javaPart.get(), name);
static auto getJavaModule =
javaPart->getClass()
->getMethod<jni::alias_ref<JTurboModule>(const std::string &)>(
"getJavaModule");
auto moduleInstance = getJavaModule(javaPart.get(), name);
if (moduleInstance) {
auto turboModule = delegate->cthis()->getTurboModule(
name, moduleInstance, jsCallInvoker, nativeCallInvoker);
turboModuleCache->insert({name, turboModule});
return turboModule;
}
if (moduleInstance) {
auto turboModule = delegate->cthis()->getTurboModule(
name, moduleInstance, jsCallInvoker, nativeCallInvoker);
turboModuleCache->insert({name, turboModule});
return turboModule;
}
return nullptr;
}));
return nullptr;
});
}
} // namespace react
@@ -21,12 +21,12 @@ namespace react {
* Public API to install the TurboModule system.
*/
TurboModuleBinding::TurboModuleBinding(
const TurboModuleProviderFunctionType &moduleProvider)
: moduleProvider_(moduleProvider) {}
const TurboModuleProviderFunctionType &&moduleProvider)
: moduleProvider_(std::move(moduleProvider)) {}
void TurboModuleBinding::install(
jsi::Runtime &runtime,
std::shared_ptr<TurboModuleBinding> binding) {
const TurboModuleProviderFunctionType &&moduleProvider) {
runtime.global().setProperty(
runtime,
"__turboModuleProxy",
@@ -34,7 +34,8 @@ void TurboModuleBinding::install(
runtime,
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
1,
[binding](
[binding =
std::make_shared<TurboModuleBinding>(std::move(moduleProvider))](
jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
@@ -43,7 +44,7 @@ void TurboModuleBinding::install(
}));
}
void TurboModuleBinding::invalidate() const {
TurboModuleBinding::~TurboModuleBinding() {
LongLivedObjectCollection::get().clear();
}
@@ -28,15 +28,10 @@ class TurboModuleBinding {
*/
static void install(
jsi::Runtime &runtime,
std::shared_ptr<TurboModuleBinding> binding);
const TurboModuleProviderFunctionType &&moduleProvider);
TurboModuleBinding(const TurboModuleProviderFunctionType &moduleProvider);
/*
* Invalidates the binding.
* Can be called in any thread.
*/
void invalidate() const;
TurboModuleBinding(const TurboModuleProviderFunctionType &&moduleProvider);
virtual ~TurboModuleBinding();
/**
* Get an TurboModule instance for the given module name.
@@ -44,8 +44,6 @@
- (void)installJSBindingWithRuntime:(facebook::jsi::Runtime *)runtime;
- (std::shared_ptr<facebook::react::TurboModule>)getModule:(const std::string &)name;
- (void)invalidate;
@end
@@ -36,7 +36,6 @@ static Class getFallbackClassFromName(const char *name)
@implementation RCTTurboModuleManager {
jsi::Runtime *_runtime;
std::shared_ptr<facebook::react::CallInvoker> _jsInvoker;
std::shared_ptr<react::TurboModuleBinding> _binding;
__weak id<RCTTurboModuleManagerDelegate> _delegate;
__weak RCTBridge *_bridge;
/**
@@ -83,38 +82,6 @@ static Class getFallbackClassFromName(const char *name)
selector:@selector(bridgeDidInvalidateModules:)
name:RCTBridgeDidInvalidateModulesNotification
object:_bridge.parentBridge];
__weak __typeof(self) weakSelf = self;
auto moduleProvider = [weakSelf](const std::string &name) -> std::shared_ptr<react::TurboModule> {
if (!weakSelf) {
return nullptr;
}
__strong __typeof(self) strongSelf = weakSelf;
auto moduleName = name.c_str();
auto moduleWasNotInitialized = ![strongSelf moduleIsInitialized:moduleName];
if (moduleWasNotInitialized) {
[strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
}
/**
* By default, all TurboModules are long-lived.
* Additionally, if a TurboModule with the name `name` isn't found, then we
* trigger an assertion failure.
*/
auto turboModule = [strongSelf provideTurboModule:moduleName];
if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) {
[strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
[strongSelf notifyAboutTurboModuleSetup:moduleName];
}
return turboModule;
};
_binding = std::make_shared<react::TurboModuleBinding>(moduleProvider);
}
return self;
}
@@ -376,12 +343,36 @@ static Class getFallbackClassFromName(const char *name)
return;
}
react::TurboModuleBinding::install(*_runtime, _binding);
}
__weak __typeof(self) weakSelf = self;
- (std::shared_ptr<facebook::react::TurboModule>)getModule:(const std::string &)name
{
return _binding->getModule(name);
react::TurboModuleBinding::install(
*_runtime, [weakSelf](const std::string &name) -> std::shared_ptr<react::TurboModule> {
if (!weakSelf) {
return nullptr;
}
__strong __typeof(self) strongSelf = weakSelf;
auto moduleName = name.c_str();
auto moduleWasNotInitialized = ![strongSelf moduleIsInitialized:moduleName];
if (moduleWasNotInitialized) {
[strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
}
/**
* By default, all TurboModules are long-lived.
* Additionally, if a TurboModule with the name `name` isn't found, then we
* trigger an assertion failure.
*/
auto turboModule = [strongSelf provideTurboModule:moduleName];
if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) {
[strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
[strongSelf notifyAboutTurboModuleSetup:moduleName];
}
return turboModule;
});
}
#pragma mark RCTTurboModuleLookupDelegate
@@ -465,8 +456,6 @@ static Class getFallbackClassFromName(const char *name)
}
_turboModuleCache.clear();
_binding->invalidate();
}
- (void)invalidate
@@ -491,8 +480,6 @@ static Class getFallbackClassFromName(const char *name)
}
_turboModuleCache.clear();
_binding->invalidate();
}
@end