Back out "Replace RAIICallbackWrapperDestroyer with AsyncCallback" (#40951)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/40951

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D50287045

fbshipit-source-id: be1a5dc2b2cc0b278d82323ed5db0b9cfdff2adc
This commit is contained in:
Arushi Kesarwani 2023-10-13 19:03:00 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 289aa10192
Коммит 60af812d9b
2 изменённых файлов: 150 добавлений и 101 удалений

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

@ -37,7 +37,6 @@ jsi::Value createPromiseAsJSIValue(
jsi::Runtime& rt,
PromiseSetupFunctionType&& func);
// Deprecated. Use AsyncCallback instead.
class RAIICallbackWrapperDestroyer {
public:
RAIICallbackWrapperDestroyer(std::weak_ptr<CallbackWrapper> callbackWrapper)

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

@ -17,7 +17,6 @@
#include <ReactCommon/TurboModulePerfLogger.h>
#include <ReactCommon/TurboModuleUtils.h>
#include <jsi/JSIDynamic.h>
#include <react/bridging/Bridging.h>
#include <react/debug/react_native_assert.h>
#include <react/jni/NativeMap.h>
#include <react/jni/ReadableNativeMap.h>
@ -64,43 +63,61 @@ struct JNIArgs {
std::vector<jobject> globalRefs_;
};
auto createJavaCallback(
jsi::Runtime& rt,
jni::local_ref<JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunction(
jsi::Function&& function,
std::shared_ptr<CallInvoker> jsInvoker) {
std::optional<AsyncCallback<>> callback(
{rt, std::move(function), std::move(jsInvoker)});
jsi::Runtime& rt,
const std::shared_ptr<CallInvoker>& jsInvoker) {
auto weakWrapper =
CallbackWrapper::createWeak(std::move(function), rt, jsInvoker);
// This needs to be a shared_ptr because:
// 1. It cannot be unique_ptr. std::function is copyable but unique_ptr is
// not.
// 2. It cannot be weak_ptr since we need this object to live on.
// 3. It cannot be a value, because that would be deleted as soon as this
// function returns.
auto callbackWrapperOwner =
std::make_shared<RAIICallbackWrapperDestroyer>(weakWrapper);
return JCxxCallbackImpl::newObjectCxxArgs(
[callback = std::move(callback)](folly::dynamic args) mutable {
if (!callback) {
LOG(FATAL) << "Callback arg cannot be called more than once";
[weakWrapper = std::move(weakWrapper),
callbackWrapperOwner = std::move(callbackWrapperOwner),
wrapperWasCalled = false](folly::dynamic responses) mutable {
if (wrapperWasCalled) {
LOG(FATAL) << "callback arg cannot be called more than once";
}
auto strongWrapper = weakWrapper.lock();
if (!strongWrapper) {
return;
}
callback->call([args = std::move(args)](
jsi::Runtime& rt, jsi::Function& jsFunction) {
std::vector<jsi::Value> jsArgs;
jsArgs.reserve(args.size());
for (const auto& val : args) {
jsArgs.emplace_back(jsi::valueFromDynamic(rt, val));
}
jsFunction.call(rt, (const jsi::Value*)jsArgs.data(), jsArgs.size());
});
callback = std::nullopt;
strongWrapper->jsInvoker().invokeAsync(
[weakWrapper = std::move(weakWrapper),
callbackWrapperOwner = std::move(callbackWrapperOwner),
responses = std::move(responses)]() {
auto strongWrapper2 = weakWrapper.lock();
if (!strongWrapper2) {
return;
}
std::vector<jsi::Value> args;
args.reserve(responses.size());
for (const auto& val : responses) {
args.emplace_back(
jsi::valueFromDynamic(strongWrapper2->runtime(), val));
}
strongWrapper2->callback().call(
strongWrapper2->runtime(),
(const jsi::Value*)args.data(),
args.size());
});
wrapperWasCalled = true;
});
}
struct JPromiseImpl : public jni::JavaClass<JPromiseImpl> {
constexpr static auto kJavaDescriptor =
"Lcom/facebook/react/bridge/PromiseImpl;";
static jni::local_ref<javaobject> create(
jni::local_ref<JCallback::javaobject> resolve,
jni::local_ref<JCallback::javaobject> reject) {
return newInstance(resolve, reject);
}
};
// This is used for generating short exception strings.
std::string stringifyJSIValue(const jsi::Value& v, jsi::Runtime* rt = nullptr) {
if (v.isUndefined()) {
@ -339,7 +356,8 @@ JNIArgs convertJSIArgsToJNIArgs(
}
jsi::Function fn = arg->getObject(rt).getFunction(rt);
jarg->l = makeGlobalIfNecessary(
createJavaCallback(rt, std::move(fn), jsInvoker).release());
createJavaCallbackFromJSIFunction(std::move(fn), rt, jsInvoker)
.release());
} else if (type == "Lcom/facebook/react/bridge/ReadableArray;") {
if (!(arg->isObject() && arg->getObject(rt).isArray(rt))) {
throw JavaTurboModuleArgumentConversionException(
@ -738,14 +756,16 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
instance_ = jni::make_weak(instance_),
moduleNameStr = name_,
methodNameStr,
id = getUniqueId()]() mutable {
id = getUniqueId()]() mutable -> void {
auto instance = instance_.lockLocal();
if (!instance) {
return;
}
// Require the env from the current scope, which may be
// different from the original invocation's scope
/**
* TODO(ramanpreet): Why do we have to require the environment
* again? Why does JNI crash when we use the env from the upper
* scope?
*/
JNIEnv* env = jni::Environment::current();
const char* moduleName = moduleNameStr.c_str();
const char* methodName = methodNameStr.c_str();
@ -769,85 +789,115 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
return jsi::Value::undefined();
}
case PromiseKind: {
// We could use AsyncPromise here, but this avoids the overhead of
// the shared_ptr for PromiseHolder
jsi::Function Promise =
runtime.global().getPropertyAsFunction(runtime, "Promise");
// The promise constructor runs its arg immediately, so this is safe
jobject javaPromise;
jsi::Value jsPromise = Promise.callAsConstructor(
jsi::Function promiseConstructorArg = jsi::Function::createFromHostFunction(
runtime,
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "fn"),
2,
[&](jsi::Runtime& runtime,
const jsi::Value&,
const jsi::Value* args,
size_t argCount) {
if (argCount != 2) {
throw jsi::JSError(runtime, "Incorrect number of arguments");
}
auto resolve = createJavaCallback(
runtime,
args[0].getObject(runtime).getFunction(runtime),
jsInvoker_);
auto reject = createJavaCallback(
runtime,
args[1].getObject(runtime).getFunction(runtime),
jsInvoker_);
javaPromise = JPromiseImpl::create(resolve, reject).release();
return jsi::Value::undefined();
}));
jobject globalPromise = env->NewGlobalRef(javaPromise);
globalRefs.push_back(globalPromise);
env->DeleteLocalRef(javaPromise);
jargs[argCount].l = globalPromise;
const char* moduleName = name_.c_str();
const char* methodName = methodNameStr.c_str();
TMPL::asyncMethodCallArgConversionEnd(moduleName, methodName);
TMPL::asyncMethodCallDispatch(moduleName, methodName);
nativeMethodCallInvoker_->invokeAsync(
methodName,
[jargs,
globalRefs,
jsi::PropNameID::forAscii(runtime, "fn"),
2,
[this,
&jargs,
&globalRefs,
argCount,
methodID,
instance_ = jni::make_weak(instance_),
moduleNameStr = name_,
methodNameStr,
id = getUniqueId()]() mutable {
auto instance = instance_.lockLocal();
if (!instance) {
return;
env](
jsi::Runtime& runtime,
const jsi::Value& thisVal,
const jsi::Value* promiseConstructorArgs,
size_t promiseConstructorArgCount) {
if (promiseConstructorArgCount != 2) {
throw std::invalid_argument("Promise fn arg count must be 2");
}
// Require the env from the current scope, which may be
// different from the original invocation's scope
JNIEnv* env = jni::Environment::current();
jsi::Function resolveJSIFn =
promiseConstructorArgs[0].getObject(runtime).getFunction(
runtime);
jsi::Function rejectJSIFn =
promiseConstructorArgs[1].getObject(runtime).getFunction(
runtime);
auto resolve = createJavaCallbackFromJSIFunction(
std::move(resolveJSIFn), runtime, jsInvoker_)
.release();
auto reject = createJavaCallbackFromJSIFunction(
std::move(rejectJSIFn), runtime, jsInvoker_)
.release();
jclass jPromiseImpl =
env->FindClass("com/facebook/react/bridge/PromiseImpl");
jmethodID jPromiseImplConstructor = env->GetMethodID(
jPromiseImpl,
"<init>",
"(Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V");
jobject promise = env->NewObject(
jPromiseImpl, jPromiseImplConstructor, resolve, reject);
const char* moduleName = moduleNameStr.c_str();
const char* methodName = methodNameStr.c_str();
TMPL::asyncMethodCallExecutionStart(moduleName, methodName, id);
env->CallVoidMethodA(instance.get(), methodID, jargs.data());
try {
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
} catch (...) {
TMPL::asyncMethodCallExecutionFail(moduleName, methodName, id);
throw;
}
for (auto globalRef : globalRefs) {
env->DeleteGlobalRef(globalRef);
}
TMPL::asyncMethodCallExecutionEnd(moduleName, methodName, id);
jobject globalPromise = env->NewGlobalRef(promise);
globalRefs.push_back(globalPromise);
env->DeleteLocalRef(promise);
jargs[argCount].l = globalPromise;
TMPL::asyncMethodCallArgConversionEnd(moduleName, methodName);
TMPL::asyncMethodCallDispatch(moduleName, methodName);
nativeMethodCallInvoker_->invokeAsync(
methodName,
[jargs,
globalRefs,
methodID,
instance_ = jni::make_weak(instance_),
moduleNameStr,
methodNameStr,
id = getUniqueId()]() mutable -> void {
auto instance = instance_.lockLocal();
if (!instance) {
return;
}
/**
* TODO(ramanpreet): Why do we have to require the
* environment again? Why does JNI crash when we use the env
* from the upper scope?
*/
JNIEnv* env = jni::Environment::current();
const char* moduleName = moduleNameStr.c_str();
const char* methodName = methodNameStr.c_str();
TMPL::asyncMethodCallExecutionStart(
moduleName, methodName, id);
env->CallVoidMethodA(instance.get(), methodID, jargs.data());
try {
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
} catch (...) {
TMPL::asyncMethodCallExecutionFail(
moduleName, methodName, id);
throw;
}
for (auto globalRef : globalRefs) {
env->DeleteGlobalRef(globalRef);
}
TMPL::asyncMethodCallExecutionEnd(moduleName, methodName, id);
});
return jsi::Value::undefined();
});
jsi::Value promise =
Promise.callAsConstructor(runtime, promiseConstructorArg);
checkJNIErrorForMethodCall();
TMPL::asyncMethodCallEnd(moduleName, methodName);
return jsPromise;
return promise;
}
default:
throw std::runtime_error(