1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #pragma once
4 
5 #include "types/CppType.h"
6 #include "types/ExpectedType.h"
7 #include "types/AnyType.h"
8 
9 #include <jsi/jsi.h>
10 #include <fbjni/fbjni.h>
11 #include <ReactCommon/TurboModuleUtils.h>
12 #include <react/bridging/LongLivedObject.h>
13 #include <react/jni/ReadableNativeArray.h>
14 #include <memory>
15 #include <vector>
16 #include <folly/dynamic.h>
17 #include <jsi/JSIDynamic.h>
18 
19 namespace jni = facebook::jni;
20 namespace jsi = facebook::jsi;
21 namespace react = facebook::react;
22 
23 namespace expo {
24 class JSIInteropModuleRegistry;
25 
26 /**
27  * A class that holds information about the exported function.
28  */
29 class MethodMetadata {
30 public:
31   /**
32    * Function name
33    */
34   std::string name;
35   /**
36    * Number of arguments
37    */
38   int args;
39   /*
40    * Whether this function is async
41    */
42   bool isAsync;
43   /**
44    * Representation of expected argument types.
45    */
46   std::vector<std::unique_ptr<AnyType>> argTypes;
47 
48   MethodMetadata(
49     std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection,
50     std::string name,
51     int args,
52     bool isAsync,
53     jni::local_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
54     jni::global_ref<jobject> &&jBodyReference
55   );
56 
57   MethodMetadata(
58     std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection,
59     std::string name,
60     int args,
61     bool isAsync,
62     std::vector<std::unique_ptr<AnyType>> &&expectedArgTypes,
63     jni::global_ref<jobject> &&jBodyReference
64   );
65 
66   // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`.
67   MethodMetadata(const MethodMetadata &) = delete;
68 
69   MethodMetadata(MethodMetadata &&other) = default;
70 
71   /**
72    * MethodMetadata owns the only reference to the Kotlin function.
73    * We have to clean that, cause it's a `global_ref`.
74    */
75   ~MethodMetadata() {
76     if (jBodyReference != nullptr) {
77       jBodyReference.release();
78     }
79   }
80 
81   /**
82    * Transforms metadata to a jsi::Function.
83    *
84    * @param runtime
85    * @param moduleRegistry
86    * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function.
87    */
88   std::shared_ptr<jsi::Function> toJSFunction(
89     jsi::Runtime &runtime,
90     JSIInteropModuleRegistry *moduleRegistry
91   );
92 
93   /**
94    * Calls the underlying Kotlin function.
95    */
96   jsi::Value callSync(
97     jsi::Runtime &rt,
98     JSIInteropModuleRegistry *moduleRegistry,
99     const jsi::Value *args,
100     size_t count
101   );
102 
103 private:
104   /**
105    * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
106    *
107    * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`.
108    * Otherwise to `JNIFunctionBody`
109    */
110   jni::global_ref<jobject> jBodyReference;
111 
112   /**
113    * To not create a jsi::Function always when we need it, we cached that value.
114    */
115   std::shared_ptr<jsi::Function> body = nullptr;
116 
117   std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_;
118 
119   jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
120 
121   jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
122 
123   jsi::Function createPromiseBody(
124     jsi::Runtime &runtime,
125     JSIInteropModuleRegistry *moduleRegistry,
126     jobjectArray globalArgs
127   );
128 
129   jobjectArray convertJSIArgsToJNI(
130     JSIInteropModuleRegistry *moduleRegistry,
131     JNIEnv *env,
132     jsi::Runtime &rt,
133     const jsi::Value *args,
134     size_t count
135   );
136 };
137 } // namespace expo
138