Delete jsi::Functions before jsi::Runtime gets deleted

Summary:
## The Problem
1. `CatalystInstanceImpl` indirectly holds on to the `jsi::Runtime`. When you destroy `CatalystInstanceImpl`, you destroy the `jsi::Runtime`. As a part of reloading React Native, we destroy and re-create `CatalystInstanceImpl`, which destroys and re-creates the `jsi::Runtime`.
2. When JS passes in a callback to a TurboModule method, we take that callback (a `jsi::Function`) and wrap it in a Java `Callback` (implemented by `JCxxCallbackImpl`). This Java `Callback`, when executed, schedules the `jsi::Function` to be invoked on a Java thread at a later point in time. **Note:** The Java NativeModule can hold on to the Java `Callback` (and, by transitivity, the `jsi::Function`) for potentially forever.
3. It is a requirement of `jsi::Runtime` that all objects associated with the Runtime (ex: `jsi::Function`) must be destroyed before the Runtime itself is destroyed. See: https://fburl.com/m3mqk6wt

### jsi.h
```
/// .................................................... In addition, to
/// make shutdown safe, destruction of objects associated with the Runtime
/// must be destroyed before the Runtime is destroyed, or from the
/// destructor of a managed HostObject or HostFunction.  Informally, this
/// means that the main source of unsafe behavior is to hold a jsi object
/// in a non-Runtime-managed object, and not clean it up before the Runtime
/// is shut down.  If your lifecycle is such that avoiding this is hard,
/// you will probably need to do use your own locks.
class Runtime {
 public:
  virtual ~Runtime();
```

Therefore, when you delete `CatalystInstanceImpl`, you could end up with a situation where the `jsi::Runtime` is destroyed before all `jsi::Function`s are destroyed. In dev, this leads the program to crash when you reload the app after having used a TurboModule method that uses callbacks.

## The Solution
If the only reference to a `HostObject` or a `HostFunction` is in the JS Heap, then the `HostObject` and `HostFunction` destructors can destroy JSI objects. The TurboModule cache is the only thing, aside from the JS Heap, that holds a reference to all C++ TurboModules. But that cache (and the entire native side of `TurboModuleManager`) is destroyed when we call `mHybridData.resetNative()` in `TurboModuleManager.onCatalystInstanceDestroy()` in D16552730. (I verified this by commenting out `mHybridData.resetNative()` and placing a breakpoint in the destructor of `JavaTurboModule`). So, when we're cleaning up `TurboModuleManager`, the only reference to a Java TurboModule is the JS Heap. Therefore, it's safe and correct for us to destroy all `jsi::Function`s created by the Java TurboModule in `~JavaTurboModule`. So, in this diff, I keep a set of all `CallbackWrappers`, and explicitly call `destroy()` on them in the `JavaTurboModule` destructor. Note that since `~JavaTurboModule` accesses `callbackWrappers_`, it must be executed on the JS Thread, since `createJavaCallbackFromJSIFunction` also accesses `callbackWrappers_` on the JS Thread.

For additional safety, I also eagerly destroyed the `jsi::Function` after it's been invoked once. I'm not yet sure if we only want JS callbacks to only ever be invoked once. So, I've created a Task to document this work: T48128233.

Reviewed By: mdvacca

Differential Revision: D16623340

fbshipit-source-id: 3a4c3efc70b9b3c8d329f19fdf4b4423c489695b
This commit is contained in:
Ramanpreet Nara 2019-08-02 17:01:55 -07:00 коммит произвёл Facebook Github Bot
Родитель 7c0b73501e
Коммит bc7c85f153
7 изменённых файлов: 375 добавлений и 145 удалений

Просмотреть файл

@ -23,6 +23,7 @@ import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.turbomodule.core.JSCallInvokerHolderImpl; import com.facebook.react.turbomodule.core.JSCallInvokerHolderImpl;
import com.facebook.react.turbomodule.core.interfaces.TurboModule; import com.facebook.react.turbomodule.core.interfaces.TurboModule;
@ -359,24 +360,56 @@ public class CatalystInstanceImpl implements CatalystInstance {
listener.onBridgeDestroyed(); listener.onBridgeDestroyed();
} }
} }
AsyncTask.execute(
new Runnable() {
@Override
public void run() {
// Kill non-UI threads from neutral third party
// potentially expensive, so don't run on UI thread
// contextHolder is used as a lock to guard against other users of the JS VM final JSIModule turboModuleManager =
// having ReactFeatureFlags.useTurboModules
// the VM destroyed underneath them, so notify them before we resetNative ? mJSIModuleRegistry.getModule(JSIModuleType.TurboModuleManager)
mJavaScriptContextHolder.clear(); : null;
mHybridData.resetNative(); getReactQueueConfiguration()
getReactQueueConfiguration().destroy(); .getJSQueueThread()
Log.d(ReactConstants.TAG, "CatalystInstanceImpl.destroy() end"); .runOnQueue(
ReactMarker.logMarker(ReactMarkerConstants.DESTROY_CATALYST_INSTANCE_END); new Runnable() {
} @Override
}); public void run() {
// We need to destroy the TurboModuleManager on the JS Thread
if (turboModuleManager != null) {
turboModuleManager.onCatalystInstanceDestroy();
}
getReactQueueConfiguration()
.getUIQueueThread()
.runOnQueue(
new Runnable() {
@Override
public void run() {
// AsyncTask.execute must be executed from the UI Thread
AsyncTask.execute(
new Runnable() {
@Override
public void run() {
// Kill non-UI threads from neutral third party
// potentially expensive, so don't run on UI thread
// contextHolder is used as a lock to guard against
// other users of the JS VM having the VM destroyed
// underneath them, so notify them before we reset
// Native
mJavaScriptContextHolder.clear();
mHybridData.resetNative();
getReactQueueConfiguration().destroy();
Log.d(
ReactConstants.TAG,
"CatalystInstanceImpl.destroy() end");
ReactMarker.logMarker(
ReactMarkerConstants.DESTROY_CATALYST_INSTANCE_END);
}
});
}
});
}
});
} }
}); });

Просмотреть файл

@ -32,7 +32,15 @@ public class JSIModuleRegistry {
} }
public void notifyJSInstanceDestroy() { public void notifyJSInstanceDestroy() {
for (JSIModuleHolder moduleHolder : mModules.values()) { for (Map.Entry<JSIModuleType, JSIModuleHolder> entry : mModules.entrySet()) {
JSIModuleType moduleType = entry.getKey();
// Don't call TurboModuleManager.onCatalystInstanceDestroy
if (moduleType == JSIModuleType.TurboModuleManager) {
continue;
}
JSIModuleHolder moduleHolder = entry.getValue();
moduleHolder.notifyJSInstanceDestroy(); moduleHolder.notifyJSInstanceDestroy();
} }
} }

Просмотреть файл

@ -22,35 +22,49 @@ static CxxModule::Callback makeTurboCxxModuleCallback(
jsi::Runtime &runtime, jsi::Runtime &runtime,
std::shared_ptr<CallbackWrapper> callbackWrapper) { std::shared_ptr<CallbackWrapper> callbackWrapper) {
return [callbackWrapper](std::vector<folly::dynamic> args) { return [callbackWrapper](std::vector<folly::dynamic> args) {
callbackWrapper->jsInvoker->invokeAsync([callbackWrapper, args]() { callbackWrapper->jsInvoker().invokeAsync([callbackWrapper, args]() {
std::vector<jsi::Value> innerArgs; std::vector<jsi::Value> innerArgs;
for (auto &a : args) { for (auto &a : args) {
innerArgs.push_back(jsi::valueFromDynamic(callbackWrapper->runtime, a)); innerArgs.push_back(
jsi::valueFromDynamic(callbackWrapper->runtime(), a));
} }
callbackWrapper->callback.call(callbackWrapper->runtime, (const jsi::Value *)innerArgs.data(), innerArgs.size()); callbackWrapper->callback().call(
callbackWrapper->runtime(),
(const jsi::Value *)innerArgs.data(),
innerArgs.size());
}); });
}; };
} }
TurboCxxModule::TurboCxxModule(std::unique_ptr<CxxModule> cxxModule, std::shared_ptr<JSCallInvoker> jsInvoker) TurboCxxModule::TurboCxxModule(
: TurboModule(cxxModule->getName(), jsInvoker), std::unique_ptr<CxxModule> cxxModule,
cxxMethods_(cxxModule->getMethods()), std::shared_ptr<JSCallInvoker> jsInvoker)
cxxModule_(std::move(cxxModule)) {} : TurboModule(cxxModule->getName(), jsInvoker),
cxxMethods_(cxxModule->getMethods()),
cxxModule_(std::move(cxxModule)) {}
jsi::Value TurboCxxModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { jsi::Value TurboCxxModule::get(
jsi::Runtime &runtime,
const jsi::PropNameID &propName) {
std::string propNameUtf8 = propName.utf8(runtime); std::string propNameUtf8 = propName.utf8(runtime);
if (propNameUtf8 == "getConstants") { if (propNameUtf8 == "getConstants") {
// This is special cased because `getConstants()` is already a part of CxxModule. // This is special cased because `getConstants()` is already a part of
// CxxModule.
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
runtime, runtime,
propName, propName,
0, 0,
[this](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { [this](
jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) {
jsi::Object result(rt); jsi::Object result(rt);
auto constants = cxxModule_->getConstants(); auto constants = cxxModule_->getConstants();
for (auto &pair : constants) { for (auto &pair : constants) {
result.setProperty(rt, pair.first.c_str(), jsi::valueFromDynamic(rt, pair.second)); result.setProperty(
rt, pair.first.c_str(), jsi::valueFromDynamic(rt, pair.second));
} }
return result; return result;
}); });
@ -62,7 +76,11 @@ jsi::Value TurboCxxModule::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
runtime, runtime,
propName, propName,
0, 0,
[this, propNameUtf8](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { [this, propNameUtf8](
jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) {
return invokeMethod(rt, VoidKind, propNameUtf8, args, count); return invokeMethod(rt, VoidKind, propNameUtf8, args, count);
}); });
} }
@ -77,7 +95,6 @@ jsi::Value TurboCxxModule::invokeMethod(
const std::string &methodName, const std::string &methodName,
const jsi::Value *args, const jsi::Value *args,
size_t count) { size_t count) {
auto it = cxxMethods_.begin(); auto it = cxxMethods_.begin();
for (; it != cxxMethods_.end(); it++) { for (; it != cxxMethods_.end(); it++) {
auto method = *it; auto method = *it;
@ -87,7 +104,8 @@ jsi::Value TurboCxxModule::invokeMethod(
} }
if (it == cxxMethods_.end()) { if (it == cxxMethods_.end()) {
throw std::runtime_error("Function '" + methodName + "' cannot be found on cxxmodule: " + name_); throw std::runtime_error(
"Function '" + methodName + "' cannot be found on cxxmodule: " + name_);
} }
auto method = *it; auto method = *it;
@ -97,23 +115,37 @@ jsi::Value TurboCxxModule::invokeMethod(
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i])); innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i]));
} }
return jsi::valueFromDynamic(runtime, method.syncFunc(std::move(innerArgs))); return jsi::valueFromDynamic(
runtime, method.syncFunc(std::move(innerArgs)));
} else if (method.func && !method.isPromise) { } else if (method.func && !method.isPromise) {
// Async method. // Async method.
CxxModule::Callback first; CxxModule::Callback first;
CxxModule::Callback second; CxxModule::Callback second;
if (count < method.callbacks) { if (count < method.callbacks) {
throw std::invalid_argument(folly::to<std::string>("Expected ", method.callbacks, throw std::invalid_argument(folly::to<std::string>(
" callbacks, but only ", count, " parameters provided")); "Expected ",
method.callbacks,
" callbacks, but only ",
count,
" parameters provided"));
} }
if (method.callbacks == 1) { if (method.callbacks == 1) {
auto wrapper = std::make_shared<CallbackWrapper>(args[count - 1].getObject(runtime).getFunction(runtime), runtime, jsInvoker_); auto wrapper = std::make_shared<CallbackWrapper>(
args[count - 1].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
first = makeTurboCxxModuleCallback(runtime, wrapper); first = makeTurboCxxModuleCallback(runtime, wrapper);
} else if (method.callbacks == 2) { } else if (method.callbacks == 2) {
auto wrapper1 = std::make_shared<CallbackWrapper>(args[count - 2].getObject(runtime).getFunction(runtime), runtime, jsInvoker_); auto wrapper1 = std::make_shared<CallbackWrapper>(
auto wrapper2 = std::make_shared<CallbackWrapper>(args[count - 1].getObject(runtime).getFunction(runtime), runtime, jsInvoker_); args[count - 2].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
auto wrapper2 = std::make_shared<CallbackWrapper>(
args[count - 1].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
first = makeTurboCxxModuleCallback(runtime, wrapper1); first = makeTurboCxxModuleCallback(runtime, wrapper1);
second = makeTurboCxxModuleCallback(runtime, wrapper2); second = makeTurboCxxModuleCallback(runtime, wrapper2);
} }
@ -125,19 +157,26 @@ jsi::Value TurboCxxModule::invokeMethod(
method.func(std::move(innerArgs), first, second); method.func(std::move(innerArgs), first, second);
} else if (method.isPromise) { } else if (method.isPromise) {
return createPromiseAsJSIValue(runtime, [method, args, count, this](jsi::Runtime &rt, std::shared_ptr<Promise> promise) { return createPromiseAsJSIValue(
auto resolveWrapper = std::make_shared<CallbackWrapper>(promise->resolve_.getFunction(rt), rt, jsInvoker_); runtime,
auto rejectWrapper = std::make_shared<CallbackWrapper>(promise->reject_.getFunction(rt), rt, jsInvoker_); [method, args, count, this](
CxxModule::Callback resolve = makeTurboCxxModuleCallback(rt, resolveWrapper); jsi::Runtime &rt, std::shared_ptr<Promise> promise) {
CxxModule::Callback reject = makeTurboCxxModuleCallback(rt, rejectWrapper); auto resolveWrapper = std::make_shared<CallbackWrapper>(
promise->resolve_.getFunction(rt), rt, jsInvoker_);
auto rejectWrapper = std::make_shared<CallbackWrapper>(
promise->reject_.getFunction(rt), rt, jsInvoker_);
CxxModule::Callback resolve =
makeTurboCxxModuleCallback(rt, resolveWrapper);
CxxModule::Callback reject =
makeTurboCxxModuleCallback(rt, rejectWrapper);
auto innerArgs = folly::dynamic::array(); auto innerArgs = folly::dynamic::array();
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
innerArgs.push_back(jsi::dynamicFromValue(rt, args[i])); innerArgs.push_back(jsi::dynamicFromValue(rt, args[i]));
} }
method.func(std::move(innerArgs), resolve, reject); method.func(std::move(innerArgs), resolve, reject);
}); });
} }
return jsi::Value::undefined(); return jsi::Value::undefined();

Просмотреть файл

@ -7,8 +7,10 @@
#pragma once #pragma once
#include <cassert>
#include <string> #include <string>
#include <folly/Optional.h>
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <ReactCommon/JSCallInvoker.h> #include <ReactCommon/JSCallInvoker.h>
@ -32,18 +34,61 @@ struct Promise {
jsi::Function reject_; jsi::Function reject_;
}; };
using PromiseSetupFunctionType = std::function<void(jsi::Runtime &rt, std::shared_ptr<Promise>)>; using PromiseSetupFunctionType =
jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, const PromiseSetupFunctionType func); std::function<void(jsi::Runtime &rt, std::shared_ptr<Promise>)>;
jsi::Value createPromiseAsJSIValue(
jsi::Runtime &rt,
const PromiseSetupFunctionType func);
// Helper for passing jsi::Function arg to other methods. // Helper for passing jsi::Function arg to other methods.
struct CallbackWrapper { class CallbackWrapper {
CallbackWrapper(jsi::Function callback, jsi::Runtime &runtime, std::shared_ptr<react::JSCallInvoker> jsInvoker) private:
: callback(std::move(callback)), struct Data {
runtime(runtime), Data(
jsInvoker(jsInvoker) {} jsi::Function callback,
jsi::Function callback; jsi::Runtime &runtime,
jsi::Runtime &runtime; std::shared_ptr<react::JSCallInvoker> jsInvoker)
std::shared_ptr<react::JSCallInvoker> jsInvoker; : callback(std::move(callback)),
runtime(runtime),
jsInvoker(std::move(jsInvoker)) {}
jsi::Function callback;
jsi::Runtime &runtime;
std::shared_ptr<react::JSCallInvoker> jsInvoker;
};
folly::Optional<Data> data_;
public:
CallbackWrapper(
jsi::Function callback,
jsi::Runtime &runtime,
std::shared_ptr<react::JSCallInvoker> jsInvoker)
: data_(Data{std::move(callback), runtime, jsInvoker}) {}
// Delete the enclosed jsi::Function
void destroy() {
data_ = folly::none;
}
bool isDestroyed() {
return !data_.hasValue();
}
jsi::Function &callback() {
assert(!isDestroyed());
return data_->callback;
}
jsi::Runtime &runtime() {
assert(!isDestroyed());
return data_->runtime;
}
react::JSCallInvoker &jsInvoker() {
assert(!isDestroyed());
return *(data_->jsInvoker);
}
}; };
} // namespace react } // namespace react

Просмотреть файл

@ -13,9 +13,7 @@
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <ReactCommon/TurboModule.h> #include <ReactCommon/TurboModule.h>
#include <ReactCommon/TurboModuleUtils.h>
#include <jsi/JSIDynamic.h> #include <jsi/JSIDynamic.h>
#include <react/jni/JCallback.h>
#include <react/jni/NativeMap.h> #include <react/jni/NativeMap.h>
#include <react/jni/ReadableNativeMap.h> #include <react/jni/ReadableNativeMap.h>
#include <react/jni/WritableNativeMap.h> #include <react/jni/WritableNativeMap.h>
@ -31,35 +29,69 @@ JavaTurboModule::JavaTurboModule(
std::shared_ptr<JSCallInvoker> jsInvoker) std::shared_ptr<JSCallInvoker> jsInvoker)
: TurboModule(name, jsInvoker), instance_(jni::make_global(instance)) {} : TurboModule(name, jsInvoker), instance_(jni::make_global(instance)) {}
jni::local_ref<JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunction( jni::local_ref<JCxxCallbackImpl::JavaPart>
JavaTurboModule::createJavaCallbackFromJSIFunction(
jsi::Function &function, jsi::Function &function,
jsi::Runtime &rt, jsi::Runtime &rt,
std::shared_ptr<JSCallInvoker> jsInvoker) { std::shared_ptr<JSCallInvoker> jsInvoker) {
auto wrapper = std::make_shared<react::CallbackWrapper>( auto wrapper = std::make_shared<react::CallbackWrapper>(
std::move(function), rt, jsInvoker); std::move(function), rt, jsInvoker);
std::function<void(folly::dynamic)> fn = [wrapper](folly::dynamic responses) { callbackWrappers_.insert(wrapper);
if (wrapper == nullptr) {
std::function<void(folly::dynamic)> fn = [this,
wrapper](folly::dynamic responses) {
if (wrapper->isDestroyed()) {
throw std::runtime_error("callback arg cannot be called more than once"); throw std::runtime_error("callback arg cannot be called more than once");
} }
std::shared_ptr<react::CallbackWrapper> rw = wrapper;
wrapper->jsInvoker->invokeAsync([rw, responses]() { wrapper->jsInvoker().invokeAsync([this, wrapper, responses]() {
if (wrapper->isDestroyed()) {
return;
}
// TODO (T43155926) valueFromDynamic already returns a Value array. Don't // TODO (T43155926) valueFromDynamic already returns a Value array. Don't
// iterate again // iterate again
jsi::Value args = jsi::valueFromDynamic(rw->runtime, responses); jsi::Value args = jsi::valueFromDynamic(wrapper->runtime(), responses);
auto argsArray = args.getObject(rw->runtime).asArray(rw->runtime); auto argsArray =
args.getObject(wrapper->runtime()).asArray(wrapper->runtime());
std::vector<jsi::Value> result; std::vector<jsi::Value> result;
for (size_t i = 0; i < argsArray.size(rw->runtime); i++) { for (size_t i = 0; i < argsArray.size(wrapper->runtime()); i++) {
result.emplace_back( result.emplace_back(
rw->runtime, argsArray.getValueAtIndex(rw->runtime, i)); wrapper->runtime(),
argsArray.getValueAtIndex(wrapper->runtime(), i));
} }
rw->callback.call( wrapper->callback().call(
rw->runtime, (const jsi::Value *)result.data(), result.size()); wrapper->runtime(), (const jsi::Value *)result.data(), result.size());
/**
* Eagerly destroy the jsi::Function since it's already been invoked.
* TODO(T48128233) Do we want callbacks to be invoked only once?
*
* NOTE: ~JavaTurboModule and this function run on the same thread.
* If you reach this point, you know that the destructor wasn't run
* because the current wrapper wasn't destroyed. Therefore, it's
* safe to access callbackWrappers_.
*/
wrapper->destroy();
callbackWrappers_.erase(wrapper);
}); });
}; };
wrapper = nullptr;
return JCxxCallbackImpl::newObjectCxxArgs(fn); return JCxxCallbackImpl::newObjectCxxArgs(fn);
} }
JavaTurboModule::~JavaTurboModule() {
/**
* Delete all jsi::Functions that haven't yet been invoked by Java.
* So long as nothing else aside from the JS heap is holding on to this
* JavaTurboModule, this destructor is guaranteed to execute before the
* jsi::Runtime is deleted.
*/
for (auto it = callbackWrappers_.begin(); it != callbackWrappers_.end();
it++) {
(*it)->destroy();
}
}
namespace { namespace {
template <typename T> template <typename T>
@ -184,7 +216,7 @@ std::vector<std::string> getMethodArgTypesFromSignature(
// needs to be done again // needs to be done again
// TODO (axe) Reuse existing implementation as needed - the exist in // TODO (axe) Reuse existing implementation as needed - the exist in
// MethodInvoker.cpp // MethodInvoker.cpp
std::vector<jvalue> convertJSIArgsToJNIArgs( std::vector<jvalue> JavaTurboModule::convertJSIArgsToJNIArgs(
JNIEnv *env, JNIEnv *env,
jsi::Runtime &rt, jsi::Runtime &rt,
std::string methodName, std::string methodName,

Просмотреть файл

@ -8,10 +8,13 @@
#pragma once #pragma once
#include <string> #include <string>
#include <unordered_set>
#include <ReactCommon/TurboModule.h> #include <ReactCommon/TurboModule.h>
#include <ReactCommon/TurboModuleUtils.h>
#include <fb/fbjni.h> #include <fb/fbjni.h>
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <react/jni/JCallback.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
@ -35,9 +38,34 @@ class JSI_EXPORT JavaTurboModule : public TurboModule {
const jsi::Value *args, const jsi::Value *args,
size_t count); size_t count);
/**
* This dtor must be called from the JS Thread, since it accesses
* callbackWrappers_, which createJavaCallbackFromJSIFunction also accesses
* from the JS Thread.
*/
virtual ~JavaTurboModule();
private: private:
jni::global_ref<JTurboModule> instance_; jni::global_ref<JTurboModule> instance_;
jclass findClass(JNIEnv *env) const; std::unordered_set<std::shared_ptr<CallbackWrapper>> callbackWrappers_;
/**
* This method must be called from the JS Thread, since it accesses
* callbackWrappers_.
*/
jni::local_ref<JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunction(
jsi::Function &function,
jsi::Runtime &rt,
std::shared_ptr<JSCallInvoker> jsInvoker);
std::vector<jvalue> convertJSIArgsToJNIArgs(
JNIEnv *env,
jsi::Runtime &rt,
std::string methodName,
std::vector<std::string> methodArgTypes,
const jsi::Value *args,
size_t count,
std::shared_ptr<JSCallInvoker> jsInvoker,
TurboModuleMethodValueKind valueKind);
}; };
} // namespace react } // namespace react

Просмотреть файл

@ -28,20 +28,24 @@ using namespace facebook;
/** /**
* All static helper functions are ObjC++ specific. * All static helper functions are ObjC++ specific.
*/ */
static jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime &runtime, NSNumber *value) { static jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime &runtime, NSNumber *value)
{
return jsi::Value((bool)[value boolValue]); return jsi::Value((bool)[value boolValue]);
} }
static jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *value) { static jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *value)
{
return jsi::Value([value doubleValue]); return jsi::Value([value doubleValue]);
} }
static jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value) { static jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value)
{
return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: ""); return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: "");
} }
static jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value); static jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
static jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value) { static jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value)
{
jsi::Object result = jsi::Object(runtime); jsi::Object result = jsi::Object(runtime);
for (NSString *k in value) { for (NSString *k in value) {
result.setProperty(runtime, [k UTF8String], convertObjCObjectToJSIValue(runtime, value[k])); result.setProperty(runtime, [k UTF8String], convertObjCObjectToJSIValue(runtime, value[k]));
@ -49,7 +53,8 @@ static jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDicti
return result; return result;
} }
static jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value) { static jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value)
{
jsi::Array result = jsi::Array(runtime, value.count); jsi::Array result = jsi::Array(runtime, value.count);
for (size_t i = 0; i < value.count; i++) { for (size_t i = 0; i < value.count; i++) {
result.setValueAtIndex(runtime, i, convertObjCObjectToJSIValue(runtime, value[i])); result.setValueAtIndex(runtime, i, convertObjCObjectToJSIValue(runtime, value[i]));
@ -57,7 +62,8 @@ static jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value
return result; return result;
} }
static std::vector<jsi::Value> convertNSArrayToStdVector(jsi::Runtime &runtime, NSArray *value) { static std::vector<jsi::Value> convertNSArrayToStdVector(jsi::Runtime &runtime, NSArray *value)
{
std::vector<jsi::Value> result; std::vector<jsi::Value> result;
for (size_t i = 0; i < value.count; i++) { for (size_t i = 0; i < value.count; i++) {
result.emplace_back(convertObjCObjectToJSIValue(runtime, value[i])); result.emplace_back(convertObjCObjectToJSIValue(runtime, value[i]));
@ -65,7 +71,8 @@ static std::vector<jsi::Value> convertNSArrayToStdVector(jsi::Runtime &runtime,
return result; return result;
} }
static jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value) { static jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value)
{
if ([value isKindOfClass:[NSString class]]) { if ([value isKindOfClass:[NSString class]]) {
return convertNSStringToJSIString(runtime, (NSString *)value); return convertNSStringToJSIString(runtime, (NSString *)value);
} else if ([value isKindOfClass:[NSNumber class]]) { } else if ([value isKindOfClass:[NSNumber class]]) {
@ -83,22 +90,35 @@ static jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value) {
return jsi::Value::undefined(); return jsi::Value::undefined();
} }
static id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<react::JSCallInvoker> jsInvoker); static id convertJSIValueToObjCObject(
static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value) { jsi::Runtime &runtime,
const jsi::Value &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker);
static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value)
{
return [NSString stringWithUTF8String:value.utf8(runtime).c_str()]; return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
} }
static NSArray *convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<react::JSCallInvoker> jsInvoker) { static NSArray *convertJSIArrayToNSArray(
jsi::Runtime &runtime,
const jsi::Array &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker)
{
size_t size = value.size(runtime); size_t size = value.size(runtime);
NSMutableArray *result = [NSMutableArray new]; NSMutableArray *result = [NSMutableArray new];
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
// Insert kCFNull when it's `undefined` value to preserve the indices. // Insert kCFNull when it's `undefined` value to preserve the indices.
[result addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker) ?: (id)kCFNull]; [result
addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker) ?: (id)kCFNull];
} }
return [result copy]; return [result copy];
} }
static NSDictionary *convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<react::JSCallInvoker> jsInvoker) { static NSDictionary *convertJSIObjectToNSDictionary(
jsi::Runtime &runtime,
const jsi::Object &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker)
{
jsi::Array propertyNames = value.getPropertyNames(runtime); jsi::Array propertyNames = value.getPropertyNames(runtime);
size_t size = propertyNames.size(runtime); size_t size = propertyNames.size(runtime);
NSMutableDictionary *result = [NSMutableDictionary new]; NSMutableDictionary *result = [NSMutableDictionary new];
@ -113,8 +133,15 @@ static NSDictionary *convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const
return [result copy]; return [result copy];
} }
static RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr<react::JSCallInvoker> jsInvoker); static RCTResponseSenderBlock convertJSIFunctionToCallback(
static id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<react::JSCallInvoker> jsInvoker) { jsi::Runtime &runtime,
const jsi::Function &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker);
static id convertJSIValueToObjCObject(
jsi::Runtime &runtime,
const jsi::Value &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker)
{
if (value.isUndefined() || value.isNull()) { if (value.isUndefined() || value.isNull()) {
return nil; return nil;
} }
@ -141,7 +168,11 @@ static id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &v
throw std::runtime_error("Unsupported jsi::jsi::Value kind"); throw std::runtime_error("Unsupported jsi::jsi::Value kind");
} }
static RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr<react::JSCallInvoker> jsInvoker) { static RCTResponseSenderBlock convertJSIFunctionToCallback(
jsi::Runtime &runtime,
const jsi::Function &value,
std::shared_ptr<react::JSCallInvoker> jsInvoker)
{
__block auto wrapper = std::make_shared<react::CallbackWrapper>(value.getFunction(runtime), runtime, jsInvoker); __block auto wrapper = std::make_shared<react::CallbackWrapper>(value.getFunction(runtime), runtime, jsInvoker);
return ^(NSArray *responses) { return ^(NSArray *responses) {
if (wrapper == nullptr) { if (wrapper == nullptr) {
@ -149,9 +180,9 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime &runtime
} }
std::shared_ptr<react::CallbackWrapper> rw = wrapper; std::shared_ptr<react::CallbackWrapper> rw = wrapper;
wrapper->jsInvoker->invokeAsync([rw, responses]() { wrapper->jsInvoker().invokeAsync([rw, responses]() {
std::vector<jsi::Value> args = convertNSArrayToStdVector(rw->runtime, responses); std::vector<jsi::Value> args = convertNSArrayToStdVector(rw->runtime(), responses);
rw->callback.call(rw->runtime, (const jsi::Value *)args.data(), args.size()); rw->callback().call(rw->runtime(), (const jsi::Value *)args.data(), args.size());
}); });
// The callback is single-use, so force release it here. // The callback is single-use, so force release it here.
@ -167,7 +198,8 @@ struct PromiseWrapper : public react::LongLivedObject {
jsi::Function resolve, jsi::Function resolve,
jsi::Function reject, jsi::Function reject,
jsi::Runtime &runtime, jsi::Runtime &runtime,
std::shared_ptr<react::JSCallInvoker> jsInvoker) { std::shared_ptr<react::JSCallInvoker> jsInvoker)
{
auto instance = std::make_shared<PromiseWrapper>(std::move(resolve), std::move(reject), runtime, jsInvoker); auto instance = std::make_shared<PromiseWrapper>(std::move(resolve), std::move(reject), runtime, jsInvoker);
// This instance needs to live longer than the caller's scope, since the resolve/reject functions may not // This instance needs to live longer than the caller's scope, since the resolve/reject functions may not
// be called immediately. Doing so keeps it alive at least until resolve/reject is called, or when the // be called immediately. Doing so keeps it alive at least until resolve/reject is called, or when the
@ -181,12 +213,15 @@ struct PromiseWrapper : public react::LongLivedObject {
jsi::Function reject, jsi::Function reject,
jsi::Runtime &runtime, jsi::Runtime &runtime,
std::shared_ptr<react::JSCallInvoker> jsInvoker) std::shared_ptr<react::JSCallInvoker> jsInvoker)
: resolveWrapper(std::make_shared<react::CallbackWrapper>(std::move(resolve), runtime, jsInvoker)), : resolveWrapper(std::make_shared<react::CallbackWrapper>(std::move(resolve), runtime, jsInvoker)),
rejectWrapper(std::make_shared<react::CallbackWrapper>(std::move(reject), runtime, jsInvoker)), rejectWrapper(std::make_shared<react::CallbackWrapper>(std::move(reject), runtime, jsInvoker)),
runtime(runtime), runtime(runtime),
jsInvoker(jsInvoker) {} jsInvoker(jsInvoker)
{
}
RCTPromiseResolveBlock resolveBlock() { RCTPromiseResolveBlock resolveBlock()
{
return ^(id result) { return ^(id result) {
if (resolveWrapper == nullptr) { if (resolveWrapper == nullptr) {
throw std::runtime_error("Promise resolve arg cannot be called more than once"); throw std::runtime_error("Promise resolve arg cannot be called more than once");
@ -195,9 +230,9 @@ struct PromiseWrapper : public react::LongLivedObject {
// Retain the resolveWrapper so that it stays alive inside the lambda. // Retain the resolveWrapper so that it stays alive inside the lambda.
std::shared_ptr<react::CallbackWrapper> retainedWrapper = resolveWrapper; std::shared_ptr<react::CallbackWrapper> retainedWrapper = resolveWrapper;
jsInvoker->invokeAsync([retainedWrapper, result]() { jsInvoker->invokeAsync([retainedWrapper, result]() {
jsi::Runtime &rt = retainedWrapper->runtime; jsi::Runtime &rt = retainedWrapper->runtime();
jsi::Value arg = convertObjCObjectToJSIValue(rt, result); jsi::Value arg = convertObjCObjectToJSIValue(rt, result);
retainedWrapper->callback.call(rt, arg); retainedWrapper->callback().call(rt, arg);
}); });
// Prevent future invocation of the same resolve() function. // Prevent future invocation of the same resolve() function.
@ -205,7 +240,8 @@ struct PromiseWrapper : public react::LongLivedObject {
}; };
} }
RCTPromiseRejectBlock rejectBlock() { RCTPromiseRejectBlock rejectBlock()
{
return ^(NSString *code, NSString *message, NSError *error) { return ^(NSString *code, NSString *message, NSError *error) {
// TODO: There is a chance `this` is no longer valid when this block executes. // TODO: There is a chance `this` is no longer valid when this block executes.
if (rejectWrapper == nullptr) { if (rejectWrapper == nullptr) {
@ -216,9 +252,9 @@ struct PromiseWrapper : public react::LongLivedObject {
std::shared_ptr<react::CallbackWrapper> retainedWrapper = rejectWrapper; std::shared_ptr<react::CallbackWrapper> retainedWrapper = rejectWrapper;
NSDictionary *jsError = RCTJSErrorFromCodeMessageAndNSError(code, message, error); NSDictionary *jsError = RCTJSErrorFromCodeMessageAndNSError(code, message, error);
jsInvoker->invokeAsync([retainedWrapper, jsError]() { jsInvoker->invokeAsync([retainedWrapper, jsError]() {
jsi::Runtime &rt = retainedWrapper->runtime; jsi::Runtime &rt = retainedWrapper->runtime();
jsi::Value arg = convertNSDictionaryToJSIObject(rt, jsError); jsi::Value arg = convertNSDictionaryToJSIObject(rt, jsError);
retainedWrapper->callback.call(rt, arg); retainedWrapper->callback().call(rt, arg);
}); });
// Prevent future invocation of the same resolve() function. // Prevent future invocation of the same resolve() function.
@ -226,23 +262,26 @@ struct PromiseWrapper : public react::LongLivedObject {
}; };
} }
void cleanup() { void cleanup()
{
resolveWrapper = nullptr; resolveWrapper = nullptr;
rejectWrapper = nullptr; rejectWrapper = nullptr;
allowRelease(); allowRelease();
} }
// CallbackWrapper is used here instead of just holding on the jsi jsi::Function in order to force release it after either // CallbackWrapper is used here instead of just holding on the jsi jsi::Function in order to force release it after
// the resolve() or the reject() is called. jsi jsi::Function does not support explicit releasing, so we need an extra // either the resolve() or the reject() is called. jsi jsi::Function does not support explicit releasing, so we need
// mechanism to control that lifecycle. // an extra mechanism to control that lifecycle.
std::shared_ptr<react::CallbackWrapper> resolveWrapper; std::shared_ptr<react::CallbackWrapper> resolveWrapper;
std::shared_ptr<react::CallbackWrapper> rejectWrapper; std::shared_ptr<react::CallbackWrapper> rejectWrapper;
jsi::Runtime &runtime; jsi::Runtime &runtime;
std::shared_ptr<react::JSCallInvoker> jsInvoker; std::shared_ptr<react::JSCallInvoker> jsInvoker;
}; };
using PromiseInvocationBlock = void (^)(jsi::Runtime& rt, std::shared_ptr<PromiseWrapper> wrapper); using PromiseInvocationBlock = void (^)(jsi::Runtime &rt, std::shared_ptr<PromiseWrapper> wrapper);
static jsi::Value createPromise(jsi::Runtime &runtime, std::shared_ptr<react::JSCallInvoker> jsInvoker, PromiseInvocationBlock invoke) { static jsi::Value
createPromise(jsi::Runtime &runtime, std::shared_ptr<react::JSCallInvoker> jsInvoker, PromiseInvocationBlock invoke)
{
if (!invoke) { if (!invoke) {
return jsi::Value::undefined(); return jsi::Value::undefined();
} }
@ -293,8 +332,8 @@ jsi::Value performMethodInvocation(
TurboModuleMethodValueKind valueKind, TurboModuleMethodValueKind valueKind,
const id<RCTTurboModule> module, const id<RCTTurboModule> module,
std::shared_ptr<JSCallInvoker> jsInvoker, std::shared_ptr<JSCallInvoker> jsInvoker,
NSMutableArray *retainedObjectsForInvocation) { NSMutableArray *retainedObjectsForInvocation)
{
__block id result; __block id result;
jsi::Runtime *rt = &runtime; jsi::Runtime *rt = &runtime;
void (^block)() = ^{ void (^block)() = ^{
@ -370,7 +409,8 @@ jsi::Value performMethodInvocation(
* Note: This is only being introduced for backward compatibility. It will be removed * Note: This is only being introduced for backward compatibility. It will be removed
* in the future. * in the future.
*/ */
NSString* ObjCTurboModule::getArgumentTypeName(NSString* methodName, int argIndex) { NSString *ObjCTurboModule::getArgumentTypeName(NSString *methodName, int argIndex)
{
if (!methodArgumentTypeNames_) { if (!methodArgumentTypeNames_) {
NSMutableDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames = [NSMutableDictionary new]; NSMutableDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames = [NSMutableDictionary new];
@ -381,7 +421,7 @@ NSString* ObjCTurboModule::getArgumentTypeName(NSString* methodName, int argInde
if (methods) { if (methods) {
for (unsigned int i = 0; i < numberOfMethods; i++) { for (unsigned int i = 0; i < numberOfMethods; i++) {
SEL s = method_getName(methods[i]); SEL s = method_getName(methods[i]);
NSString* mName = NSStringFromSelector(s); NSString *mName = NSStringFromSelector(s);
if (![mName hasPrefix:@"__rct_export__"]) { if (![mName hasPrefix:@"__rct_export__"]) {
continue; continue;
} }
@ -393,7 +433,7 @@ NSString* ObjCTurboModule::getArgumentTypeName(NSString* methodName, int argInde
NSArray<RCTMethodArgument *> *arguments; NSArray<RCTMethodArgument *> *arguments;
NSString *otherMethodName = RCTParseMethodSignature(methodInfo->objcName, &arguments); NSString *otherMethodName = RCTParseMethodSignature(methodInfo->objcName, &arguments);
NSMutableArray* argumentTypes = [NSMutableArray arrayWithCapacity:[arguments count]]; NSMutableArray *argumentTypes = [NSMutableArray arrayWithCapacity:[arguments count]];
for (int j = 0; j < [arguments count]; j += 1) { for (int j = 0; j < [arguments count]; j += 1) {
[argumentTypes addObject:arguments[j].type]; [argumentTypes addObject:arguments[j].type];
} }
@ -417,16 +457,18 @@ NSString* ObjCTurboModule::getArgumentTypeName(NSString* methodName, int argInde
} }
NSInvocation *ObjCTurboModule::getMethodInvocation( NSInvocation *ObjCTurboModule::getMethodInvocation(
jsi::Runtime &runtime, jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind, TurboModuleMethodValueKind valueKind,
const id<RCTTurboModule> module, const id<RCTTurboModule> module,
std::shared_ptr<JSCallInvoker> jsInvoker, std::shared_ptr<JSCallInvoker> jsInvoker,
const std::string& methodName, const std::string &methodName,
SEL selector, SEL selector,
const jsi::Value *args, const jsi::Value *args,
size_t count, size_t count,
NSMutableArray *retainedObjectsForInvocation) { NSMutableArray *retainedObjectsForInvocation)
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[module class] instanceMethodSignatureForSelector:selector]]; {
NSInvocation *inv =
[NSInvocation invocationWithMethodSignature:[[module class] instanceMethodSignatureForSelector:selector]];
[inv setSelector:selector]; [inv setSelector:selector];
NSMethodSignature *methodSignature = [[module class] instanceMethodSignatureForSelector:selector]; NSMethodSignature *methodSignature = [[module class] instanceMethodSignatureForSelector:selector];
@ -504,7 +546,7 @@ NSInvocation *ObjCTurboModule::getMethodInvocation(
* Convert objects using RCTConvert. * Convert objects using RCTConvert.
*/ */
if (objCArgType[0] == _C_ID) { if (objCArgType[0] == _C_ID) {
NSString* argumentType = getArgumentTypeName(methodNameNSString, i); NSString *argumentType = getArgumentTypeName(methodNameNSString, i);
if (argumentType != nil) { if (argumentType != nil) {
NSString *rctConvertMethodName = [NSString stringWithFormat:@"%@:", argumentType]; NSString *rctConvertMethodName = [NSString stringWithFormat:@"%@:", argumentType];
SEL rctConvertSelector = NSSelectorFromString(rctConvertMethodName); SEL rctConvertSelector = NSSelectorFromString(rctConvertMethodName);
@ -556,8 +598,9 @@ ObjCTurboModule::ObjCTurboModule(
const std::string &name, const std::string &name,
id<RCTTurboModule> instance, id<RCTTurboModule> instance,
std::shared_ptr<JSCallInvoker> jsInvoker) std::shared_ptr<JSCallInvoker> jsInvoker)
: TurboModule(name, jsInvoker), : TurboModule(name, jsInvoker), instance_(instance)
instance_(instance) {} {
}
jsi::Value ObjCTurboModule::invokeObjCMethod( jsi::Value ObjCTurboModule::invokeObjCMethod(
jsi::Runtime &runtime, jsi::Runtime &runtime,
@ -568,39 +611,41 @@ jsi::Value ObjCTurboModule::invokeObjCMethod(
size_t count) size_t count)
{ {
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2]; NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
NSInvocation *inv = getMethodInvocation(runtime, valueKind, instance_, jsInvoker_, methodName, selector, args, count, retainedObjectsForInvocation); NSInvocation *inv = getMethodInvocation(
runtime, valueKind, instance_, jsInvoker_, methodName, selector, args, count, retainedObjectsForInvocation);
if (valueKind == PromiseKind) { if (valueKind == PromiseKind) {
// Promise return type is special cased today, i.e. it needs extra 2 function args for resolve() and reject(), to // Promise return type is special cased today, i.e. it needs extra 2 function args for resolve() and reject(), to
// be passed to the actual ObjC++ class method. // be passed to the actual ObjC++ class method.
return createPromise( return createPromise(runtime, jsInvoker_, ^(jsi::Runtime &rt, std::shared_ptr<PromiseWrapper> wrapper) {
runtime, RCTPromiseResolveBlock resolveBlock = wrapper->resolveBlock();
jsInvoker_, RCTPromiseRejectBlock rejectBlock = wrapper->rejectBlock();
^(jsi::Runtime &rt, std::shared_ptr<PromiseWrapper> wrapper) { [inv setArgument:(void *)&resolveBlock atIndex:count + 2];
RCTPromiseResolveBlock resolveBlock = wrapper->resolveBlock(); [inv setArgument:(void *)&rejectBlock atIndex:count + 3];
RCTPromiseRejectBlock rejectBlock = wrapper->rejectBlock(); [retainedObjectsForInvocation addObject:resolveBlock];
[inv setArgument:(void *)&resolveBlock atIndex:count + 2]; [retainedObjectsForInvocation addObject:rejectBlock];
[inv setArgument:(void *)&rejectBlock atIndex:count + 3]; // The return type becomes void in the ObjC side.
[retainedObjectsForInvocation addObject:resolveBlock]; performMethodInvocation(rt, inv, VoidKind, instance_, jsInvoker_, retainedObjectsForInvocation);
[retainedObjectsForInvocation addObject:rejectBlock]; });
// The return type becomes void in the ObjC side.
performMethodInvocation(rt, inv, VoidKind, instance_, jsInvoker_, retainedObjectsForInvocation);
});
} }
return performMethodInvocation(runtime, inv, valueKind, instance_, jsInvoker_, retainedObjectsForInvocation); return performMethodInvocation(runtime, inv, valueKind, instance_, jsInvoker_, retainedObjectsForInvocation);
} }
BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, int argIndex) { BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, int argIndex)
return methodArgConversionSelectors_ && methodArgConversionSelectors_[methodName] && ![methodArgConversionSelectors_[methodName][argIndex] isEqual:[NSNull null]]; {
return methodArgConversionSelectors_ && methodArgConversionSelectors_[methodName] &&
![methodArgConversionSelectors_[methodName][argIndex] isEqual:[NSNull null]];
} }
SEL ObjCTurboModule::getMethodArgConversionSelector(NSString *methodName, int argIndex) { SEL ObjCTurboModule::getMethodArgConversionSelector(NSString *methodName, int argIndex)
{
assert(hasMethodArgConversionSelector(methodName, argIndex)); assert(hasMethodArgConversionSelector(methodName, argIndex));
return (SEL)((NSValue *)methodArgConversionSelectors_[methodName][argIndex]).pointerValue; return (SEL)((NSValue *)methodArgConversionSelectors_[methodName][argIndex]).pointerValue;
} }
void ObjCTurboModule::setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName) { void ObjCTurboModule::setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName)
{
if (!methodArgConversionSelectors_) { if (!methodArgConversionSelectors_) {
methodArgConversionSelectors_ = [NSMutableDictionary new]; methodArgConversionSelectors_ = [NSMutableDictionary new];
} }