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 jni::local_ref<JSIInteropModuleRegistry::jhybriddata>
18 JSIInteropModuleRegistry::initHybrid(jni::alias_ref<jhybridobject> jThis) {
19   return makeCxxInstance(jThis);
20 }
21 
22 void JSIInteropModuleRegistry::registerNatives() {
23   registerHybrid({
24                    makeNativeMethod("initHybrid", JSIInteropModuleRegistry::initHybrid),
25                    makeNativeMethod("installJSI", JSIInteropModuleRegistry::installJSI),
26                    makeNativeMethod("installJSIForTests",
27                                     JSIInteropModuleRegistry::installJSIForTests),
28                    makeNativeMethod("evaluateScript", JSIInteropModuleRegistry::evaluateScript),
29                    makeNativeMethod("global", JSIInteropModuleRegistry::global),
30                    makeNativeMethod("createObject", JSIInteropModuleRegistry::createObject),
31                    makeNativeMethod("drainJSEventLoop", JSIInteropModuleRegistry::drainJSEventLoop),
32                  });
33 }
34 
35 JSIInteropModuleRegistry::JSIInteropModuleRegistry(jni::alias_ref<jhybridobject> jThis)
36   : javaPart_(jni::make_global(jThis)) {}
37 
38 void JSIInteropModuleRegistry::installJSI(
39   jlong jsRuntimePointer,
40   jni::alias_ref<JNIDeallocator::javaobject> jniDeallocator,
41   jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
42   jni::alias_ref<react::CallInvokerHolder::javaobject> nativeInvokerHolder
43 ) {
44   this->jniDeallocator = jni::make_global(jniDeallocator);
45 
46   auto runtime = reinterpret_cast<jsi::Runtime *>(jsRuntimePointer);
47 
48   jsRegistry = std::make_unique<JSReferencesCache>(*runtime);
49 
50   runtimeHolder = std::make_shared<JavaScriptRuntime>(
51     this,
52     runtime,
53     jsInvokerHolder->cthis()->getCallInvoker(),
54     nativeInvokerHolder->cthis()->getCallInvoker()
55   );
56 
57   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
58   auto expoModulesObject = jsi::Object::createFromHostObject(*runtime, expoModules);
59 
60   // Define the `global.expo.modules` object.
61   runtimeHolder
62     ->getMainObject()
63     ->setProperty(
64       *runtime,
65       "modules",
66       expoModulesObject
67     );
68 
69   // Also define `global.ExpoModules` for backwards compatibility (used before SDK47, can be removed in SDK48).
70   runtime
71     ->global()
72     .setProperty(
73       *runtime,
74       "ExpoModules",
75       expoModulesObject
76     );
77 }
78 
79 void JSIInteropModuleRegistry::installJSIForTests(
80   jni::alias_ref<JNIDeallocator::javaobject> jniDeallocator
81 ) {
82 #if !UNIT_TEST
83   throw std::logic_error("The function is only available when UNIT_TEST is defined.");
84 #else
85   this->jniDeallocator = jni::make_global(jniDeallocator);
86 
87   runtimeHolder = std::make_shared<JavaScriptRuntime>(this);
88   jsi::Runtime &jsiRuntime = runtimeHolder->get();
89 
90   jsRegistry = std::make_unique<JSReferencesCache>(jsiRuntime);
91 
92   auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
93   auto expoModulesObject = jsi::Object::createFromHostObject(jsiRuntime, expoModules);
94 
95   runtimeHolder
96     ->getMainObject()
97     ->setProperty(
98       jsiRuntime,
99       "modules",
100       std::move(expoModulesObject)
101     );
102 #endif // !UNIT_TEST
103 }
104 
105 jni::local_ref<JavaScriptModuleObject::javaobject>
106 JSIInteropModuleRegistry::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
107   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
108     ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>(
109       std::string)>(
110       "getJavaScriptModuleObject"
111     );
112 
113   return method(javaPart_, moduleName);
114 }
115 
116 jni::local_ref<jni::JArrayClass<jni::JString>>
117 JSIInteropModuleRegistry::callGetJavaScriptModulesNames() const {
118   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
119     ->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>(
120       "getJavaScriptModulesName"
121     );
122   return method(javaPart_);
123 }
124 
125 bool JSIInteropModuleRegistry::callHasModule(const std::string &moduleName) const {
126   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
127     ->getMethod<jboolean(std::string)>(
128       "hasModule"
129     );
130   return (bool) method(javaPart_, moduleName);
131 }
132 
133 jni::local_ref<JavaScriptModuleObject::javaobject>
134 JSIInteropModuleRegistry::getModule(const std::string &moduleName) const {
135   return callGetJavaScriptModuleObjectMethod(moduleName);
136 }
137 
138 bool JSIInteropModuleRegistry::hasModule(const std::string &moduleName) const {
139   return callHasModule(moduleName);
140 }
141 
142 jni::local_ref<jni::JArrayClass<jni::JString>> JSIInteropModuleRegistry::getModulesName() const {
143   return callGetJavaScriptModulesNames();
144 }
145 
146 jni::local_ref<JavaScriptValue::javaobject> JSIInteropModuleRegistry::evaluateScript(
147   jni::JString script
148 ) {
149   return runtimeHolder->evaluateScript(script.toStdString());
150 }
151 
152 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::global() {
153   return runtimeHolder->global();
154 }
155 
156 jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::createObject() {
157   return runtimeHolder->createObject();
158 }
159 
160 void JSIInteropModuleRegistry::drainJSEventLoop() {
161   runtimeHolder->drainJSEventLoop();
162 }
163 
164 void JSIInteropModuleRegistry::registerSharedObject(
165   jni::local_ref<jobject> native,
166   jni::local_ref<JavaScriptObject::javaobject> js
167 ) {
168   const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
169     ->getMethod<void(jni::local_ref<jobject>, jni::local_ref<JavaScriptObject::javaobject>)>(
170       "registerSharedObject"
171     );
172   method(javaPart_, std::move(native), std::move(js));
173 }
174 } // namespace expo
175