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