1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #pragma once
4 
5 #include <fbjni/fbjni.h>
6 #include <jsi/jsi.h>
7 #include <react/jni/ReadableNativeArray.h>
8 #include <jni/JCallback.h>
9 
10 #include <unordered_map>
11 
12 #include "MethodMetadata.h"
13 #include "JNIFunctionBody.h"
14 #include "types/ExpectedType.h"
15 
16 namespace jni = facebook::jni;
17 namespace jsi = facebook::jsi;
18 namespace react = facebook::react;
19 
20 namespace expo {
21 class JSIInteropModuleRegistry;
22 
23 class JavaScriptModuleObject;
24 
25 void decorateObjectWithFunctions(
26   jsi::Runtime &runtime,
27   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
28   jsi::Object *jsObject,
29   JavaScriptModuleObject *objectData
30 );
31 
32 void decorateObjectWithProperties(
33   jsi::Runtime &runtime,
34   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
35   jsi::Object *jsObject,
36   JavaScriptModuleObject *objectData
37 );
38 
39 void decorateObjectWithConstants(
40   jsi::Runtime &runtime,
41   JSIInteropModuleRegistry *jsiInteropModuleRegistry,
42   jsi::Object *jsObject,
43   JavaScriptModuleObject *objectData
44 );
45 
46 /**
47  * A CPP part of the module.
48  *
49  * Right now objects of this class are stored by the ModuleHolder to ensure they will live
50  * as long as the RN context.
51  */
52 class JavaScriptModuleObject : public jni::HybridClass<JavaScriptModuleObject> {
53 public:
54   static auto constexpr
55     kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptModuleObject;";
56   static auto constexpr TAG = "JavaScriptModuleObject";
57 
58   static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
59 
60   static void registerNatives();
61 
62   /**
63    * Pointer to the module registry interop.
64    */
65   JSIInteropModuleRegistry *jsiInteropModuleRegistry;
66 
67   /**
68    * Returns a cached instance of jsi::Object representing this module.
69    * @param runtime
70    * @return Wrapped instance of JavaScriptModuleObject::HostObject
71    */
72   std::shared_ptr<jsi::Object> getJSIObject(jsi::Runtime &runtime);
73 
74   /**
75    * Exports constants that will be assigned to the underlying HostObject.
76    */
77   void exportConstants(jni::alias_ref<react::NativeMap::javaobject> constants);
78 
79   /**
80    * Registers a sync function.
81    * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method.
82    */
83   void registerSyncFunction(
84     jni::alias_ref<jstring> name,
85     jboolean takesOwner,
86     jint args,
87     jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
88     jni::alias_ref<JNIFunctionBody::javaobject> body
89   );
90 
91   /**
92    * Registers a async function.
93    * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method.
94    */
95   void registerAsyncFunction(
96     jni::alias_ref<jstring> name,
97     jboolean takesOwner,
98     jint args,
99     jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
100     jni::alias_ref<JNIAsyncFunctionBody::javaobject> body
101   );
102 
103   void registerClass(
104     jni::alias_ref<jstring> name,
105     jni::alias_ref<JavaScriptModuleObject::javaobject> classObject,
106     jboolean takesOwner,
107     jint args,
108     jni::alias_ref<jni::JArrayClass<ExpectedType>> expectedArgTypes,
109     jni::alias_ref<JNIFunctionBody::javaobject> body
110   );
111 
112   void registerViewPrototype(
113     jni::alias_ref<JavaScriptModuleObject::javaobject> viewPrototype
114   );
115 
116   /**
117    * Registers a property
118    * @param name of the property
119    * @param desiredType of the setter argument
120    * @param getter body for the get method - can be nullptr
121    * @param setter body for the set method - can be nullptr
122    */
123   void registerProperty(
124     jni::alias_ref<jstring> name,
125     jboolean getterTakesOwner,
126     jni::alias_ref<jni::JArrayClass<ExpectedType>> getterExpectedArgsTypes,
127     jni::alias_ref<JNIFunctionBody::javaobject> getter,
128     jboolean setterTakesOwner,
129     jni::alias_ref<jni::JArrayClass<ExpectedType>> setterExpectedArgsTypes,
130     jni::alias_ref<JNIFunctionBody::javaobject> setter
131   );
132 
133 private:
134   explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis);
135 
136 private:
137   friend HybridBase;
138 
139   friend void decorateObjectWithFunctions(
140     jsi::Runtime &runtime,
141     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
142     jsi::Object *jsObject,
143     JavaScriptModuleObject *objectData
144   );
145 
146   friend void decorateObjectWithProperties(
147     jsi::Runtime &runtime,
148     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
149     jsi::Object *jsObject,
150     JavaScriptModuleObject *objectData
151   );
152 
153   friend void decorateObjectWithConstants(
154     jsi::Runtime &runtime,
155     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
156     jsi::Object *jsObject,
157     JavaScriptModuleObject *objectData
158   );
159 
160   /**
161    * A reference to the `jsi::Object`.
162    * Simple we cached that value to return the same object each time.
163    * It's a weak reference because the JS runtime holds the actual object.
164    * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore.
165    */
166   std::weak_ptr<jsi::Object> jsiObject;
167   jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_;
168 
169   /**
170    * Metadata map that stores information about all available methods on this module.
171    */
172   std::unordered_map<std::string, MethodMetadata> methodsMetadata;
173 
174   /**
175    * A constants map.
176    */
177   std::unordered_map<std::string, folly::dynamic> constants;
178 
179   /**
180    * A registry of properties
181    * The first MethodMetadata points to the getter and the second one to the setter.
182    */
183   std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties;
184 
185   std::map<
186     std::string,
187     std::pair<jni::global_ref<JavaScriptModuleObject::javaobject>, MethodMetadata>
188   > classes;
189 
190   jni::global_ref<JavaScriptModuleObject::javaobject> viewPrototype;
191 };
192 } // namespace expo
193