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    * Whether this function takes owner
37    */
38   bool takesOwner;
39   /**
40    * Number of arguments
41    */
42   int args;
43   /**
44    * Whether this function is async
45    */
46   bool isAsync;
47   /**
48    * Representation of expected argument types.
49    */
50   std::vector<std::unique_ptr<AnyType>> argTypes;
51 
52   MethodMetadata(
53     std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection,
54     std::string name,
55     bool takesOwner,
56     int args,
57     bool isAsync,
58     jni::local_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
59     jni::global_ref<jobject> &&jBodyReference
60   );
61 
62   MethodMetadata(
63     std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection,
64     std::string name,
65     bool takesOwner,
66     int args,
67     bool isAsync,
68     std::vector<std::unique_ptr<AnyType>> &&expectedArgTypes,
69     jni::global_ref<jobject> &&jBodyReference
70   );
71 
72   // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`.
73   MethodMetadata(const MethodMetadata &) = delete;
74 
75   MethodMetadata(MethodMetadata &&other) = default;
76 
77   /**
78    * MethodMetadata owns the only reference to the Kotlin function.
79    * We have to clean that, cause it's a `global_ref`.
80    */
81   ~MethodMetadata() {
82     if (jBodyReference != nullptr) {
83       jBodyReference.release();
84     }
85   }
86 
87   /**
88    * Transforms metadata to a jsi::Function.
89    *
90    * @param runtime
91    * @param moduleRegistry
92    * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function.
93    */
94   std::shared_ptr<jsi::Function> toJSFunction(
95     jsi::Runtime &runtime,
96     JSIInteropModuleRegistry *moduleRegistry
97   );
98 
99   /**
100    * Calls the underlying Kotlin function.
101    */
102   jsi::Value callSync(
103     jsi::Runtime &rt,
104     JSIInteropModuleRegistry *moduleRegistry,
105     const jsi::Value &thisValue,
106     const jsi::Value *args,
107     size_t count
108   );
109 
110   jni::local_ref<jobject> callJNISync(
111     JNIEnv *env,
112     jsi::Runtime &rt,
113     JSIInteropModuleRegistry *moduleRegistry,
114     const jsi::Value &thisValue,
115     const jsi::Value *args,
116     size_t count
117   );
118 
119 private:
120   /**
121    * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
122    *
123    * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`.
124    * Otherwise to `JNIFunctionBody`
125    */
126   jni::global_ref<jobject> jBodyReference;
127 
128   /**
129    * To not create a jsi::Function always when we need it, we cached that value.
130    */
131   std::shared_ptr<jsi::Function> body = nullptr;
132 
133   std::weak_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_;
134 
135   jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
136 
137   jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
138 
139   jsi::Function createPromiseBody(
140     jsi::Runtime &runtime,
141     JSIInteropModuleRegistry *moduleRegistry,
142     jobjectArray globalArgs
143   );
144 
145   jobjectArray convertJSIArgsToJNI(
146     JSIInteropModuleRegistry *moduleRegistry,
147     JNIEnv *env,
148     jsi::Runtime &rt,
149     const jsi::Value &thisValue,
150     const jsi::Value *args,
151     size_t count
152   );
153 };
154 } // namespace expo
155