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