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