1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JSIInteropModuleRegistry.h"
4 #include "ExpoModulesHostObject.h"
5 #include "JavaReferencesCache.h"
6 #include "JSReferencesCache.h"
7 
8 #include <fbjni/detail/Meta.h>
9 #include <fbjni/fbjni.h>
10 
11 #include <memory>
12 
13 namespace jni = facebook::jni;
14 namespace jsi = facebook::jsi;
15 
16 namespace expo {
17 
18 namespace {
19 
20 #if REACT_NATIVE_TARGET_VERSION >= 73
21 std::shared_ptr<NativeMethodCallInvokerCompatible> getNativeMethodCallInvokerHolderCompatible(
22   jni::alias_ref<NativeMethodCallInvokerHolderCompatible::javaobject> holder) {
23   return holder->cthis()->getNativeMethodCallInvoker();
24 }
25 #else
26 
27 std::shared_ptr<NativeMethodCallInvokerCompatible> getNativeMethodCallInvokerHolderCompatible(
28   jni::alias_ref<NativeMethodCallInvokerHolderCompatible::javaobject> holder) {
29   return holder->cthis()->getCallInvoker();
30 }
31 
32 #endif
33 
34 } // namespace
35 
36 jni::local_ref<JSIInteropModuleRegistry::jhybriddata>
37 JSIInteropModuleRegistry::initHybrid(jni::alias_ref<jhybridobject> jThis) {
38   return makeCxxInstance(jThis);
39 }
40 
41 void JSIInteropModuleRegistry::registerNatives() {
42   registerHybrid({
43                    makeNativeMethod("initHybrid", JSIInteropModuleRegistry::initHybrid),
44                    makeNativeMethod("installJSI", JSIInteropModuleRegistry::installJSI),
45                    makeNativeMethod("installJSIForTests",
46                                     JSIInteropModuleRegistry::installJSIForTests),
47                    makeNativeMethod("evaluateScript", JSIInteropModuleRegistry::evaluateScript),
48                    makeNativeMethod("global", JSIInteropModuleRegistry::global),
49                    makeNativeMethod("createObject", JSIInteropModuleRegistry::createObject),
50                    makeNativeMethod("drainJSEventLoop", JSIInteropModuleRegistry::drainJSEventLoop),
51                  });
52 }
53 
54 JSIInteropModuleRegistry::JSIInteropModuleRegistry(jni::alias_ref<jhybridobject> jThis)
55   : javaPart_(jni::make_global(jThis)) {}
56 
57 void JSIInteropModuleRegistry::installJSI(
58   jlong jsRuntimePointer,
59   jni::alias_ref<JNIDeallocator::javaobject> jniDeallocator,
60   jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
61   jni::alias_ref<NativeMethodCallInvokerHolderCompatible::javaobject> nativeInvokerHolder
62 ) {
63   this->jniDeallocator = jni::make_global(jniDeallocator);
64 
65   auto runtime = reinterpret_cast<jsi::Runtime *>(jsRuntimePointer);
66 
67   jsRegistry = std::make_unique<JSReferencesCache>(*runtime);
68 
69   runtimeHolder = std::make_shared<JavaScriptRuntime>(
70     this,
71     runtime,
72     jsInvokerHolder->cthis()->getCallInvoker(),
73     getNativeMethodCallInvokerHolderCompatible(nativeInvokerHolder)
74   );
75 
76   runtimeHolder->installMainObject();
77 
78   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
79   auto expoModulesObject = jsi::Object::createFromHostObject(*runtime, expoModules);
80 
81   // Define the `global.expo.modules` object.
82   runtimeHolder
83     ->getMainObject()
84     ->setProperty(
85       *runtime,
86       "modules",
87       expoModulesObject
88     );
89 
90   // Also define `global.ExpoModules` for backwards compatibility (used before SDK47, can be removed in SDK48).
91   runtime
92     ->global()
93     .setProperty(
94       *runtime,
95       "ExpoModules",
96       expoModulesObject
97     );
98 }
99 
100 void JSIInteropModuleRegistry::installJSIForTests(
101   jni::alias_ref<JNIDeallocator::javaobject> jniDeallocator
102 ) {
103 #if !UNIT_TEST
104   throw std::logic_error("The function is only available when UNIT_TEST is defined.");
105 #else
106   this->jniDeallocator = jni::make_global(jniDeallocator);
107 
108   runtimeHolder = std::make_shared<JavaScriptRuntime>(this);
109   jsi::Runtime &jsiRuntime = runtimeHolder->get();
110 
111   jsRegistry = std::make_unique<JSReferencesCache>(jsiRuntime);
112 
113   runtimeHolder->installMainObject();
114 
115   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
116   auto expoModulesObject = jsi::Object::createFromHostObject(jsiRuntime, expoModules);
117 
118   runtimeHolder
119     ->getMainObject()
120     ->setProperty(
121       jsiRuntime,
122       "modules",
123       std::move(expoModulesObject)
124     );
125 #endif // !UNIT_TEST
126 }
127 
128 jni::local_ref<JavaScriptModuleObject::javaobject>
129 JSIInteropModuleRegistry::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
130   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
131     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>(
132       std::string)>(
133       "getJavaScriptModuleObject"
134     );
135 
136   return method(javaPart_, moduleName);
137 }
138 
139 jni::local_ref<JavaScriptModuleObject::javaobject>
140 JSIInteropModuleRegistry::callGetCoreModuleObject() const {
141   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
142     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>()>(
143       "getCoreModuleObject"
144     );
145 
146   return method(javaPart_);
147 }
148 
149 jni::local_ref<jni::JArrayClass<jni::JString>>
150 JSIInteropModuleRegistry::callGetJavaScriptModulesNames() const {
151   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
152     ->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>(
153       "getJavaScriptModulesName"
154     );
155   return method(javaPart_);
156 }
157 
158 bool JSIInteropModuleRegistry::callHasModule(const std::string &moduleName) const {
159   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
160     ->getMethod<jboolean(std::string)>(
161       "hasModule"
162     );
163   return (bool) method(javaPart_, moduleName);
164 }
165 
166 jni::local_ref<JavaScriptModuleObject::javaobject>
167 JSIInteropModuleRegistry::getModule(const std::string &moduleName) const {
168   return callGetJavaScriptModuleObjectMethod(moduleName);
169 }
170 
171 jni::local_ref<JavaScriptModuleObject::javaobject> JSIInteropModuleRegistry::getCoreModule() const {
172   return callGetCoreModuleObject();
173 }
174 
175 bool JSIInteropModuleRegistry::hasModule(const std::string &moduleName) const {
176   return callHasModule(moduleName);
177 }
178 
179 jni::local_ref<jni::JArrayClass<jni::JString>> JSIInteropModuleRegistry::getModulesName() const {
180   return callGetJavaScriptModulesNames();
181 }
182 
183 jni::local_ref<JavaScriptValue::javaobject> JSIInteropModuleRegistry::evaluateScript(
184   jni::JString script
185 ) {
186   return runtimeHolder->evaluateScript(script.toStdString());
187 }
188 
189 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::global() {
190   return runtimeHolder->global();
191 }
192 
193 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::createObject() {
194   return runtimeHolder->createObject();
195 }
196 
197 void JSIInteropModuleRegistry::drainJSEventLoop() {
198   runtimeHolder->drainJSEventLoop();
199 }
200 
201 void JSIInteropModuleRegistry::registerSharedObject(
202   jni::local_ref<jobject> native,
203   jni::local_ref<JavaScriptObject::javaobject> js
204 ) {
205   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
206     ->getMethod<void(jni::local_ref<jobject>, jni::local_ref<JavaScriptObject::javaobject>)>(
207       "registerSharedObject"
208     );
209   method(javaPart_, std::move(native), std::move(js));
210 }
211 } // namespace expo
212