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/jni/ReadableNativeArray.h>
13 #include <memory>
14 #include <vector>
15 #include <folly/dynamic.h>
16 #include <jsi/JSIDynamic.h>
17 
18 namespace jni = facebook::jni;
19 namespace jsi = facebook::jsi;
20 namespace react = facebook::react;
21 
22 namespace expo {
23 class JSIInteropModuleRegistry;
24 
25 /**
26  * A class that holds information about the exported function.
27  */
28 class MethodMetadata {
29 public:
30   /**
31    * Function name
32    */
33   std::string name;
34   /**
35    * Whether this function takes owner
36    */
37   bool takesOwner;
38   /**
39    * Number of arguments
40    */
41   int args;
42   /**
43    * Whether this function is async
44    */
45   bool isAsync;
46   /**
47    * Representation of expected argument types.
48    */
49   std::vector<std::unique_ptr<AnyType>> argTypes;
50 
51   MethodMetadata(
52     std::string name,
53     bool takesOwner,
54     int args,
55     bool isAsync,
56     jni::local_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
57     jni::global_ref<jobject> &&jBodyReference
58   );
59 
60   MethodMetadata(
61     std::string name,
62     bool takesOwner,
63     int args,
64     bool isAsync,
65     std::vector<std::unique_ptr<AnyType>> &&expectedArgTypes,
66     jni::global_ref<jobject> &&jBodyReference
67   );
68 
69   // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`.
70   MethodMetadata(const MethodMetadata &) = delete;
71 
72   MethodMetadata(MethodMetadata &&other) = default;
73 
74   /**
75    * Transforms metadata to a jsi::Function.
76    *
77    * @param runtime
78    * @param moduleRegistry
79    * @return shared ptr to the jsi::Function that wrapped the underlying Kotlin's function.
80    */
81   std::shared_ptr<jsi::Function> toJSFunction(
82     jsi::Runtime &runtime,
83     JSIInteropModuleRegistry *moduleRegistry
84   );
85 
86   /**
87    * Calls the underlying Kotlin function.
88    */
89   jsi::Value callSync(
90     jsi::Runtime &rt,
91     JSIInteropModuleRegistry *moduleRegistry,
92     const jsi::Value &thisValue,
93     const jsi::Value *args,
94     size_t count
95   );
96 
97   jni::local_ref<jobject> callJNISync(
98     JNIEnv *env,
99     jsi::Runtime &rt,
100     JSIInteropModuleRegistry *moduleRegistry,
101     const jsi::Value &thisValue,
102     const jsi::Value *args,
103     size_t count
104   );
105 
106 private:
107   /**
108    * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
109    *
110    * In case when `isAsync` is `true`, this variable will point to `JNIAsyncFunctionBody`.
111    * Otherwise to `JNIFunctionBody`
112    */
113   jni::global_ref<jobject> jBodyReference;
114 
115   /**
116    * To not create a jsi::Function always when we need it, we cached that value.
117    */
118   std::shared_ptr<jsi::Function> body = nullptr;
119 
120   jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
121 
122   jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
123 
124   jsi::Function createPromiseBody(
125     jsi::Runtime &runtime,
126     JSIInteropModuleRegistry *moduleRegistry,
127     jobjectArray globalArgs
128   );
129 
130   jobjectArray convertJSIArgsToJNI(
131     JSIInteropModuleRegistry *moduleRegistry,
132     JNIEnv *env,
133     jsi::Runtime &rt,
134     const jsi::Value &thisValue,
135     const jsi::Value *args,
136     size_t count
137   );
138 };
139 } // namespace expo
140