1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JavaReferencesCache.h"
4 
5 namespace expo {
6 std::shared_ptr<JavaReferencesCache> JavaReferencesCache::instance() {
7   static std::shared_ptr<JavaReferencesCache> singleton{new JavaReferencesCache};
8   return singleton;
9 }
10 
11 void JavaReferencesCache::loadJClasses(JNIEnv *env) {
12   loadJClass(env, "java/lang/Double", {
13     {"<init>", "(D)V"}
14   });
15 
16   loadJClass(env, "java/lang/Boolean", {
17     {"<init>", "(Z)V"}
18   });
19 
20   loadJClass(env, "java/lang/Integer", {
21     {"<init>", "(I)V"}
22   });
23 
24   loadJClass(env, "java/lang/Long", {
25     {"<init>", "(J)V"}
26   });
27 
28   loadJClass(env, "java/lang/Float", {
29     {"<init>", "(F)V"}
30   });
31 
32   loadJClass(env, "com/facebook/react/bridge/PromiseImpl", {
33     {"<init>", "(Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V"}
34   });
35 
36   loadJClass(env, "expo/modules/kotlin/jni/PromiseImpl", {
37     {"<init>", "(Lexpo/modules/kotlin/jni/JavaCallback;Lexpo/modules/kotlin/jni/JavaCallback;)V"}
38   });
39 
40   loadJClass(env, "java/lang/Object", {});
41   loadJClass(env, "java/lang/String", {});
42   loadJClass(env, "expo/modules/kotlin/jni/JavaScriptObject", {});
43   loadJClass(env, "expo/modules/kotlin/jni/JavaScriptValue", {});
44   loadJClass(env, "expo/modules/kotlin/jni/JavaScriptTypedArray", {});
45   loadJClass(env, "com/facebook/react/bridge/ReadableNativeArray", {});
46   loadJClass(env, "com/facebook/react/bridge/ReadableNativeMap", {});
47   loadJClass(env, "com/facebook/react/bridge/WritableNativeArray", {});
48   loadJClass(env, "com/facebook/react/bridge/WritableNativeMap", {});
49 }
50 
51 void JavaReferencesCache::loadJClass(
52   JNIEnv *env,
53   const std::string &name,
54   const std::vector<std::pair<std::string, std::string>> &methodsNames
55 ) {
56   // Note this clazz variable points to a leaked global reference.
57   // This is appropriate for classes that are never unloaded which is any class in an Android app.
58   auto clazz = (jclass) env->NewGlobalRef(env->FindClass(name.c_str()));
59 
60   MethodHashMap methods;
61   methods.reserve(methodsNames.size());
62 
63   for (auto &method: methodsNames) {
64     methods.insert(
65       {method, env->GetMethodID(clazz, method.first.c_str(), method.second.c_str())}
66     );
67   }
68 
69   jClassRegistry.insert(
70     {name, CachedJClass(clazz, std::move(methods))}
71   );
72 }
73 
74 JavaReferencesCache::CachedJClass &JavaReferencesCache::getJClass(
75   const std::string &className
76 ) {
77   return jClassRegistry.at(className);
78 }
79 
80 JavaReferencesCache::CachedJClass &JavaReferencesCache::getOrLoadJClass(
81   JNIEnv *env,
82   const std::string &className
83 ) {
84   auto result = jClassRegistry.find(className);
85   if (result == jClassRegistry.end()) {
86     loadJClass(env, className, {});
87     return jClassRegistry.at(className);
88   }
89 
90   return result->second;
91 }
92 
93 jmethodID JavaReferencesCache::CachedJClass::getMethod(
94   const std::string &name,
95   const std::string &signature
96 ) {
97   return methods.at({name, signature});
98 }
99 
100 JavaReferencesCache::CachedJClass::CachedJClass(
101   jclass clazz,
102   MethodHashMap methods
103 ) : clazz(clazz), methods(std::move(methods)) {}
104 } // namespace expo
105