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