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   /**
66    * MethodMetadata owns the only reference to the Kotlin function.
67    * We have to clean that, cause it's a `global_ref`.
68    */
69   ~MethodMetadata() {
70     jBodyReference.release();
71   }
72 
73   /**
74    * Transforms metadata to a jsi::Function.
75    *
76    * @param runtime
77    * @param moduleRegistry
78    * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function.
79    */
80   std::shared_ptr<jsi::Function> toJSFunction(
81     jsi::Runtime &runtime,
82     JSIInteropModuleRegistry *moduleRegistry
83   );
84 
85 private:
86   /**
87    * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
88    *
89    * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`.
90    * Otherwise to `JNIFunctionBody`
91    */
92   jni::global_ref<jobject> jBodyReference;
93 
94   /**
95    * To not create a jsi::Function always when we need it, we cached that value.
96    */
97   std::shared_ptr<jsi::Function> body = nullptr;
98 
99   jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
100 
101   jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
102 
103   jsi::Function createPromiseBody(
104     jsi::Runtime &runtime,
105     JSIInteropModuleRegistry *moduleRegistry,
106     jni::local_ref<jni::JArrayClass<jobject>::javaobject> &&args
107   );
108 
109   std::vector<jvalue> convertJSIArgsToJNI(
110     JSIInteropModuleRegistry *moduleRegistry,
111     JNIEnv *env,
112     jsi::Runtime &rt,
113     const jsi::Value *args,
114     size_t count
115   );
116 };
117 } // namespace expo
118