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   TYPED_ARRAY = 1 << 7
33 };
34 
35 /**
36  * A class that holds information about the exported function.
37  */
38 class MethodMetadata {
39 public:
40   /**
41    * Function name
42    */
43   std::string name;
44   /**
45    * Number of arguments
46    */
47   int args;
48   /*
49    * Whether this function is async
50    */
51   bool isAsync;
52 
53   std::unique_ptr<int[]> desiredTypes;
54 
55   MethodMetadata(
56     std::string name,
57     int args,
58     bool isAsync,
59     std::unique_ptr<int[]> desiredTypes,
60     jni::global_ref<jobject> &&jBodyReference
61   );
62 
63   // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`.
64   MethodMetadata(const MethodMetadata &) = delete;
65 
66   MethodMetadata(MethodMetadata &&other) = default;
67 
68   /**
69    * MethodMetadata owns the only reference to the Kotlin function.
70    * We have to clean that, cause it's a `global_ref`.
71    */
72   ~MethodMetadata() {
73     if (jBodyReference != nullptr) {
74       jBodyReference.release();
75     }
76   }
77 
78   /**
79    * Transforms metadata to a jsi::Function.
80    *
81    * @param runtime
82    * @param moduleRegistry
83    * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function.
84    */
85   std::shared_ptr<jsi::Function> toJSFunction(
86     jsi::Runtime &runtime,
87     JSIInteropModuleRegistry *moduleRegistry
88   );
89 
90   /**
91    * Calls the underlying Kotlin function.
92    */
93   jsi::Value callSync(
94     jsi::Runtime &rt,
95     JSIInteropModuleRegistry *moduleRegistry,
96     const jsi::Value *args,
97     size_t count
98   );
99 
100 private:
101   /**
102    * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
103    *
104    * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`.
105    * Otherwise to `JNIFunctionBody`
106    */
107   jni::global_ref<jobject> jBodyReference;
108 
109   /**
110    * To not create a jsi::Function always when we need it, we cached that value.
111    */
112   std::shared_ptr<jsi::Function> body = nullptr;
113 
114   jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
115 
116   jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
117 
118   jsi::Function createPromiseBody(
119     jsi::Runtime &runtime,
120     JSIInteropModuleRegistry *moduleRegistry,
121     std::vector<jvalue> &&args
122   );
123 
124   std::vector<jvalue> convertJSIArgsToJNI(
125     JSIInteropModuleRegistry *moduleRegistry,
126     JNIEnv *env,
127     jsi::Runtime &rt,
128     const jsi::Value *args,
129     size_t count,
130     bool returnGlobalReferences
131   );
132 };
133 } // namespace expo
134