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   /**
110    * Registers a property
111    * @param name of the property
112    * @param desiredType of the setter argument
113    * @param getter body for the get method - can be nullptr
114    * @param setter body for the set method - can be nullptr
115    */
116   void registerProperty(
117     jni::alias_ref<jstring> name,
118     jni::alias_ref<ExpectedType> expectedArgType,
119     jni::alias_ref<JNIFunctionBody::javaobject> getter,
120     jni::alias_ref<JNIFunctionBody::javaobject> setter
121   );
122 
123 private:
124   explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis);
125 
126 private:
127   friend HybridBase;
128 
129   friend void decorateObjectWithFunctions(
130     jsi::Runtime &runtime,
131     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
132     jsi::Object *jsObject,
133     JavaScriptModuleObject *objectData
134   );
135 
136   friend void decorateObjectWithProperties(
137     jsi::Runtime &runtime,
138     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
139     jsi::Object *jsObject,
140     JavaScriptModuleObject *objectData
141   );
142 
143   friend void decorateObjectWithConstants(
144     jsi::Runtime &runtime,
145     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
146     jsi::Object *jsObject,
147     JavaScriptModuleObject *objectData
148   );
149 
150   /**
151    * A reference to the `jsi::Object`.
152    * Simple we cached that value to return the same object each time.
153    * It's a weak reference because the JS runtime holds the actual object.
154    * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore.
155    */
156   std::weak_ptr<jsi::Object> jsiObject;
157   jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_;
158 
159   /**
160    * Metadata map that stores information about all available methods on this module.
161    */
162   std::unordered_map<std::string, MethodMetadata> methodsMetadata;
163 
164   /**
165    * A constants map.
166    */
167   std::unordered_map<std::string, folly::dynamic> constants;
168 
169   /**
170    * A registry of properties
171    * The first MethodMetadata points to the getter and the second one to the setter.
172    */
173   std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties;
174 
175   /**
176    * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module.
177    */
178   std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_;
179 
180   std::map<std::string, jni::global_ref<JavaScriptModuleObject::javaobject>> classes;
181 };
182 } // namespace expo
183