17
17
#include < ReactCommon/TurboModulePerfLogger.h>
18
18
#include < ReactCommon/TurboModuleUtils.h>
19
19
#include < jsi/JSIDynamic.h>
20
+ #include < react/bridging/Bridging.h>
20
21
#include < react/debug/react_native_assert.h>
21
22
#include < react/jni/NativeMap.h>
22
23
#include < react/jni/ReadableNativeMap.h>
@@ -63,61 +64,43 @@ struct JNIArgs {
63
64
std::vector<jobject> globalRefs_;
64
65
};
65
66
66
- jni::local_ref<JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunction (
67
- jsi::Function&& function,
67
+ auto createJavaCallback (
68
68
jsi::Runtime& rt,
69
- const std::shared_ptr<CallInvoker>& jsInvoker) {
70
- auto weakWrapper =
71
- CallbackWrapper::createWeak (std::move (function), rt, jsInvoker);
72
-
73
- // This needs to be a shared_ptr because:
74
- // 1. It cannot be unique_ptr. std::function is copyable but unique_ptr is
75
- // not.
76
- // 2. It cannot be weak_ptr since we need this object to live on.
77
- // 3. It cannot be a value, because that would be deleted as soon as this
78
- // function returns.
79
- auto callbackWrapperOwner =
80
- std::make_shared<RAIICallbackWrapperDestroyer>(weakWrapper);
81
-
69
+ jsi::Function&& function,
70
+ std::shared_ptr<CallInvoker> jsInvoker) {
71
+ std::optional<AsyncCallback<>> callback (
72
+ {rt, std::move (function), std::move (jsInvoker)});
82
73
return JCxxCallbackImpl::newObjectCxxArgs (
83
- [weakWrapper = std::move (weakWrapper),
84
- callbackWrapperOwner = std::move (callbackWrapperOwner),
85
- wrapperWasCalled = false ](folly::dynamic responses) mutable {
86
- if (wrapperWasCalled) {
87
- LOG (FATAL) << " callback arg cannot be called more than once" ;
88
- }
89
-
90
- auto strongWrapper = weakWrapper.lock ();
91
- if (!strongWrapper) {
74
+ [callback = std::move (callback)](folly::dynamic args) mutable {
75
+ if (!callback) {
76
+ LOG (FATAL) << " Callback arg cannot be called more than once" ;
92
77
return ;
93
78
}
94
79
95
- strongWrapper->jsInvoker ().invokeAsync (
96
- [weakWrapper = std::move (weakWrapper),
97
- callbackWrapperOwner = std::move (callbackWrapperOwner),
98
- responses = std::move (responses)]() {
99
- auto strongWrapper2 = weakWrapper.lock ();
100
- if (!strongWrapper2) {
101
- return ;
102
- }
103
-
104
- std::vector<jsi::Value> args;
105
- args.reserve (responses.size ());
106
- for (const auto & val : responses) {
107
- args.emplace_back (
108
- jsi::valueFromDynamic (strongWrapper2->runtime (), val));
109
- }
110
-
111
- strongWrapper2->callback ().call (
112
- strongWrapper2->runtime (),
113
- (const jsi::Value*)args.data (),
114
- args.size ());
115
- });
116
-
117
- wrapperWasCalled = true ;
80
+ callback->call ([args = std::move (args)](
81
+ jsi::Runtime& rt, jsi::Function& jsFunction) {
82
+ std::vector<jsi::Value> jsArgs;
83
+ jsArgs.reserve (args.size ());
84
+ for (const auto & val : args) {
85
+ jsArgs.emplace_back (jsi::valueFromDynamic (rt, val));
86
+ }
87
+ jsFunction.call (rt, (const jsi::Value*)jsArgs.data (), jsArgs.size ());
88
+ });
89
+ callback = std::nullopt;
118
90
});
119
91
}
120
92
93
+ struct JPromiseImpl : public jni ::JavaClass<JPromiseImpl> {
94
+ constexpr static auto kJavaDescriptor =
95
+ " Lcom/facebook/react/bridge/PromiseImpl;" ;
96
+
97
+ static jni::local_ref<javaobject> create (
98
+ jni::local_ref<JCallback::javaobject> resolve,
99
+ jni::local_ref<JCallback::javaobject> reject) {
100
+ return newInstance (resolve, reject);
101
+ }
102
+ };
103
+
121
104
// This is used for generating short exception strings.
122
105
std::string stringifyJSIValue (const jsi::Value& v, jsi::Runtime* rt = nullptr ) {
123
106
if (v.isUndefined ()) {
@@ -356,8 +339,7 @@ JNIArgs convertJSIArgsToJNIArgs(
356
339
}
357
340
jsi::Function fn = arg->getObject (rt).getFunction (rt);
358
341
jarg->l = makeGlobalIfNecessary (
359
- createJavaCallbackFromJSIFunction (std::move (fn), rt, jsInvoker)
360
- .release ());
342
+ createJavaCallback (rt, std::move (fn), jsInvoker).release ());
361
343
} else if (type == " Lcom/facebook/react/bridge/ReadableArray;" ) {
362
344
if (!(arg->isObject () && arg->getObject (rt).isArray (rt))) {
363
345
throw JavaTurboModuleArgumentConversionException (
@@ -756,16 +738,14 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
756
738
instance_ = jni::make_weak (instance_),
757
739
moduleNameStr = name_,
758
740
methodNameStr,
759
- id = getUniqueId ()]() mutable -> void {
741
+ id = getUniqueId ()]() mutable {
760
742
auto instance = instance_.lockLocal ();
761
743
if (!instance) {
762
744
return ;
763
745
}
764
- /* *
765
- * TODO(ramanpreet): Why do we have to require the environment
766
- * again? Why does JNI crash when we use the env from the upper
767
- * scope?
768
- */
746
+
747
+ // Require the env from the current scope, which may be
748
+ // different from the original invocation's scope
769
749
JNIEnv* env = jni::Environment::current ();
770
750
const char * moduleName = moduleNameStr.c_str ();
771
751
const char * methodName = methodNameStr.c_str ();
@@ -789,115 +769,85 @@ jsi::Value JavaTurboModule::invokeJavaMethod(
789
769
return jsi::Value::undefined ();
790
770
}
791
771
case PromiseKind: {
772
+ // We could use AsyncPromise here, but this avoids the overhead of
773
+ // the shared_ptr for PromiseHolder
792
774
jsi::Function Promise =
793
775
runtime.global ().getPropertyAsFunction (runtime, " Promise" );
794
776
795
- jsi::Function promiseConstructorArg = jsi::Function::createFromHostFunction (
777
+ // The promise constructor runs its arg immediately, so this is safe
778
+ jobject javaPromise;
779
+ jsi::Value jsPromise = Promise.callAsConstructor (
796
780
runtime,
797
- jsi::PropNameID::forAscii (runtime, " fn" ),
798
- 2 ,
799
- [this ,
800
- &jargs,
801
- &globalRefs,
802
- argCount,
781
+ jsi::Function::createFromHostFunction (
782
+ runtime,
783
+ jsi::PropNameID::forAscii (runtime, " fn" ),
784
+ 2 ,
785
+ [&](jsi::Runtime& runtime,
786
+ const jsi::Value&,
787
+ const jsi::Value* args,
788
+ size_t argCount) {
789
+ if (argCount != 2 ) {
790
+ throw jsi::JSError (runtime, " Incorrect number of arguments" );
791
+ }
792
+
793
+ auto resolve = createJavaCallback (
794
+ runtime,
795
+ args[0 ].getObject (runtime).getFunction (runtime),
796
+ jsInvoker_);
797
+ auto reject = createJavaCallback (
798
+ runtime,
799
+ args[1 ].getObject (runtime).getFunction (runtime),
800
+ jsInvoker_);
801
+ javaPromise = JPromiseImpl::create (resolve, reject).release ();
802
+
803
+ return jsi::Value::undefined ();
804
+ }));
805
+
806
+ jobject globalPromise = env->NewGlobalRef (javaPromise);
807
+ globalRefs.push_back (globalPromise);
808
+ env->DeleteLocalRef (javaPromise);
809
+ jargs[argCount].l = globalPromise;
810
+
811
+ const char * moduleName = name_.c_str ();
812
+ const char * methodName = methodNameStr.c_str ();
813
+ TMPL::asyncMethodCallArgConversionEnd (moduleName, methodName);
814
+
815
+ TMPL::asyncMethodCallDispatch (moduleName, methodName);
816
+ nativeMethodCallInvoker_->invokeAsync (
817
+ methodName,
818
+ [jargs,
819
+ globalRefs,
803
820
methodID,
821
+ instance_ = jni::make_weak (instance_),
804
822
moduleNameStr = name_,
805
823
methodNameStr,
806
- env](
807
- jsi::Runtime& runtime,
808
- const jsi::Value& thisVal,
809
- const jsi::Value* promiseConstructorArgs,
810
- size_t promiseConstructorArgCount) {
811
- if (promiseConstructorArgCount != 2 ) {
812
- throw std::invalid_argument (" Promise fn arg count must be 2" );
824
+ id = getUniqueId ()]() mutable {
825
+ auto instance = instance_.lockLocal ();
826
+ if (!instance) {
827
+ return ;
813
828
}
814
829
815
- jsi::Function resolveJSIFn =
816
- promiseConstructorArgs[0 ].getObject (runtime).getFunction (
817
- runtime);
818
- jsi::Function rejectJSIFn =
819
- promiseConstructorArgs[1 ].getObject (runtime).getFunction (
820
- runtime);
821
-
822
- auto resolve = createJavaCallbackFromJSIFunction (
823
- std::move (resolveJSIFn), runtime, jsInvoker_)
824
- .release ();
825
- auto reject = createJavaCallbackFromJSIFunction (
826
- std::move (rejectJSIFn), runtime, jsInvoker_)
827
- .release ();
828
-
829
- jclass jPromiseImpl =
830
- env->FindClass (" com/facebook/react/bridge/PromiseImpl" );
831
- jmethodID jPromiseImplConstructor = env->GetMethodID (
832
- jPromiseImpl,
833
- " <init>" ,
834
- " (Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V" );
835
-
836
- jobject promise = env->NewObject (
837
- jPromiseImpl, jPromiseImplConstructor, resolve, reject);
838
-
830
+ // Require the env from the current scope, which may be
831
+ // different from the original invocation's scope
832
+ JNIEnv* env = jni::Environment::current ();
839
833
const char * moduleName = moduleNameStr.c_str ();
840
834
const char * methodName = methodNameStr.c_str ();
835
+ TMPL::asyncMethodCallExecutionStart (moduleName, methodName, id);
836
+ env->CallVoidMethodA (instance.get (), methodID, jargs.data ());
837
+ try {
838
+ FACEBOOK_JNI_THROW_PENDING_EXCEPTION ();
839
+ } catch (...) {
840
+ TMPL::asyncMethodCallExecutionFail (moduleName, methodName, id);
841
+ throw ;
842
+ }
841
843
842
- jobject globalPromise = env->NewGlobalRef (promise);
843
-
844
- globalRefs.push_back (globalPromise);
845
- env->DeleteLocalRef (promise);
846
-
847
- jargs[argCount].l = globalPromise;
848
- TMPL::asyncMethodCallArgConversionEnd (moduleName, methodName);
849
- TMPL::asyncMethodCallDispatch (moduleName, methodName);
850
-
851
- nativeMethodCallInvoker_->invokeAsync (
852
- methodName,
853
- [jargs,
854
- globalRefs,
855
- methodID,
856
- instance_ = jni::make_weak (instance_),
857
- moduleNameStr,
858
- methodNameStr,
859
- id = getUniqueId ()]() mutable -> void {
860
- auto instance = instance_.lockLocal ();
861
-
862
- if (!instance) {
863
- return ;
864
- }
865
- /* *
866
- * TODO(ramanpreet): Why do we have to require the
867
- * environment again? Why does JNI crash when we use the env
868
- * from the upper scope?
869
- */
870
- JNIEnv* env = jni::Environment::current ();
871
- const char * moduleName = moduleNameStr.c_str ();
872
- const char * methodName = methodNameStr.c_str ();
873
-
874
- TMPL::asyncMethodCallExecutionStart (
875
- moduleName, methodName, id);
876
- env->CallVoidMethodA (instance.get (), methodID, jargs.data ());
877
- try {
878
- FACEBOOK_JNI_THROW_PENDING_EXCEPTION ();
879
- } catch (...) {
880
- TMPL::asyncMethodCallExecutionFail (
881
- moduleName, methodName, id);
882
- throw ;
883
- }
884
-
885
- for (auto globalRef : globalRefs) {
886
- env->DeleteGlobalRef (globalRef);
887
- }
888
- TMPL::asyncMethodCallExecutionEnd (moduleName, methodName, id);
889
- });
890
-
891
- return jsi::Value::undefined ();
844
+ for (auto globalRef : globalRefs) {
845
+ env->DeleteGlobalRef (globalRef);
846
+ }
847
+ TMPL::asyncMethodCallExecutionEnd (moduleName, methodName, id);
892
848
});
893
-
894
- jsi::Value promise =
895
- Promise.callAsConstructor (runtime, promiseConstructorArg);
896
- checkJNIErrorForMethodCall ();
897
-
898
849
TMPL::asyncMethodCallEnd (moduleName, methodName);
899
-
900
- return promise;
850
+ return jsPromise;
901
851
}
902
852
default :
903
853
throw std::runtime_error (
0 commit comments