xref: /aosp_15_r20/external/fbjni/cxx/fbjni/fbjni.cpp (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <fbjni/fbjni.h>
18 
19 #include <mutex>
20 #include <vector>
21 
22 #include <fbjni/detail/utf8.h>
23 
24 namespace facebook {
25 namespace jni {
26 
initialize(JavaVM * vm,std::function<void ()> && init_fn)27 jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
28   // TODO (t7832883): DTRT when we have exception pointers
29   static auto error_msg = std::string{"Failed to initialize fbjni"};
30   static bool error_occured = [vm] {
31     bool retVal = false;
32     try {
33       Environment::initialize(vm);
34     } catch (std::exception& ex) {
35       retVal = true;
36       try {
37         error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
38       } catch (...) {
39         // Ignore, we already have a fall back message
40       }
41     } catch (...) {
42       retVal = true;
43     }
44     return retVal;
45   }();
46 
47   try {
48     if (error_occured) {
49       throw std::runtime_error(error_msg);
50     }
51 
52     init_fn();
53   } catch (const std::exception& e) {
54     FBJNI_LOGE("error %s", e.what());
55     translatePendingCppExceptionToJavaException();
56   } catch (...) {
57     translatePendingCppExceptionToJavaException();
58     // So Java will handle the translated exception, fall through and
59     // return a good version number.
60   }
61   return JNI_VERSION_1_6;
62 }
63 
64 namespace detail {
65 
findClass(JNIEnv * env,const char * name)66 jclass findClass(JNIEnv* env, const char* name) {
67   if (!env) {
68     throw std::runtime_error("Unable to retrieve JNIEnv*.");
69   }
70   jclass cls = env->FindClass(name);
71   FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
72   return cls;
73 }
74 
75 } // namespace detail
76 
findClassLocal(const char * name)77 local_ref<JClass> findClassLocal(const char* name) {
78   return adopt_local(detail::findClass(detail::currentOrNull(), name));
79 }
80 
findClassStatic(const char * name)81 alias_ref<JClass> findClassStatic(const char* name) {
82   JNIEnv* env = detail::currentOrNull();
83   auto cls = adopt_local(detail::findClass(env, name));
84   auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
85   FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
86   return wrap_alias(leaking_ref);
87 }
88 
89 // jstring
90 // /////////////////////////////////////////////////////////////////////////////////////////
91 
toStdString() const92 std::string JString::toStdString() const {
93   const auto env = Environment::current();
94   auto utf16String = JStringUtf16Extractor(env, self());
95   return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
96 }
97 
toU16String() const98 std::u16string JString::toU16String() const {
99   const auto env = Environment::current();
100   auto utf16String = JStringUtf16Extractor(env, self());
101   if (!utf16String.chars() || utf16String.length() == 0) {
102     return {};
103   }
104   return std::u16string(
105       reinterpret_cast<const char16_t*>(utf16String.chars()),
106       utf16String.length());
107 }
108 
make_jstring(const char * utf8)109 local_ref<JString> make_jstring(const char* utf8) {
110   if (!utf8) {
111     return {};
112   }
113   const auto env = Environment::current();
114   size_t len;
115   size_t modlen =
116       detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
117   jstring result;
118   if (modlen == len) {
119     // The only difference between utf8 and modifiedUTF8 is in encoding 4-byte
120     // UTF8 chars and '\0' that is encoded on 2 bytes.
121     //
122     // Since modifiedUTF8-encoded string can be no shorter than it's UTF8
123     // conterpart we know that if those two strings are of the same length we
124     // don't need to do any conversion -> no 4-byte chars nor '\0'.
125     result = env->NewStringUTF(utf8);
126   } else {
127     auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
128     detail::utf8ToModifiedUTF8(
129         reinterpret_cast<const uint8_t*>(utf8),
130         len,
131         reinterpret_cast<uint8_t*>(modified.data()),
132         modified.size());
133     result = env->NewStringUTF(modified.data());
134   }
135   FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
136   return adopt_local(result);
137 }
138 
make_jstring(const std::u16string & utf16)139 local_ref<JString> make_jstring(const std::u16string& utf16) {
140   if (utf16.empty()) {
141     return {};
142   }
143   const auto env = Environment::current();
144   static_assert(
145       sizeof(jchar) == sizeof(std::u16string::value_type),
146       "Expecting jchar to be the same size as std::u16string::CharT");
147   jstring result = env->NewString(
148       reinterpret_cast<const jchar*>(utf16.c_str()), utf16.size());
149   FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
150   return adopt_local(result);
151 }
152 
153 // JniPrimitiveArrayFunctions
154 // //////////////////////////////////////////////////////////////////////
155 
156 #pragma push_macro("DEFINE_PRIMITIVE_METHODS")
157 #undef DEFINE_PRIMITIVE_METHODS
158 #define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME)               \
159                                                                       \
160   template <>                                                         \
161   TYPE* JPrimitiveArray<TYPE##Array>::getElements(jboolean* isCopy) { \
162     auto env = Environment::current();                                \
163     TYPE* res = env->Get##NAME##ArrayElements(self(), isCopy);        \
164     FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                           \
165     return res;                                                       \
166   }                                                                   \
167                                                                       \
168   template <>                                                         \
169   void JPrimitiveArray<TYPE##Array>::releaseElements(                 \
170       TYPE* elements, jint mode) {                                    \
171     auto env = Environment::current();                                \
172     env->Release##NAME##ArrayElements(self(), elements, mode);        \
173     FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                           \
174   }                                                                   \
175                                                                       \
176   template <>                                                         \
177   void JPrimitiveArray<TYPE##Array>::getRegion(                       \
178       jsize start, jsize length, TYPE* buf) {                         \
179     auto env = Environment::current();                                \
180     env->Get##NAME##ArrayRegion(self(), start, length, buf);          \
181     FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                           \
182   }                                                                   \
183                                                                       \
184   template <>                                                         \
185   void JPrimitiveArray<TYPE##Array>::setRegion(                       \
186       jsize start, jsize length, const TYPE* elements) {              \
187     auto env = Environment::current();                                \
188     env->Set##NAME##ArrayRegion(self(), start, length, elements);     \
189     FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                           \
190   }                                                                   \
191                                                                       \
192   local_ref<TYPE##Array> make_##SMALLNAME##_array(jsize size) {       \
193     auto array = Environment::current()->New##NAME##Array(size);      \
194     FACEBOOK_JNI_THROW_EXCEPTION_IF(!array);                          \
195     return adopt_local(array);                                        \
196   }                                                                   \
197                                                                       \
198   template <>                                                         \
199   local_ref<TYPE##Array> JArray##NAME::newArray(size_t count) {       \
200     return make_##SMALLNAME##_array(count);                           \
201   }
202 
203 DEFINE_PRIMITIVE_METHODS(jboolean, Boolean, boolean)
204 DEFINE_PRIMITIVE_METHODS(jbyte, Byte, byte)
205 DEFINE_PRIMITIVE_METHODS(jchar, Char, char)
206 DEFINE_PRIMITIVE_METHODS(jshort, Short, short)
207 DEFINE_PRIMITIVE_METHODS(jint, Int, int)
208 DEFINE_PRIMITIVE_METHODS(jlong, Long, long)
209 DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
210 DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
211 #pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
212 
213 namespace detail {
214 
getNativePointer() const215 detail::BaseHybridClass* HybridDestructor::getNativePointer() const {
216   static auto pointerField =
217       javaClassStatic()->getField<jlong>("mNativePointer");
218   auto* value =
219       reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
220   if (!value) {
221     throwNewJavaException(
222         "java/lang/NullPointerException", "java.lang.NullPointerException");
223   }
224   return value;
225 }
226 
setNativePointer(std::unique_ptr<detail::BaseHybridClass> new_value)227 void HybridDestructor::setNativePointer(
228     std::unique_ptr<detail::BaseHybridClass> new_value) {
229   static auto pointerField =
230       javaClassStatic()->getField<jlong>("mNativePointer");
231   auto old_value = std::unique_ptr<detail::BaseHybridClass>(
232       reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField)));
233   if (new_value && old_value) {
234     FBJNI_LOGF("Attempt to set C++ native pointer twice");
235   }
236   setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
237 }
238 
239 } // namespace detail
240 
241 // Internal debug
242 // /////////////////////////////////////////////////////////////////////////////////
243 
244 namespace internal {
245 
246 ReferenceStats g_reference_stats;
247 
reset()248 void facebook::jni::internal::ReferenceStats::reset() noexcept {
249   locals_deleted = globals_deleted = weaks_deleted = 0;
250 }
251 
252 } // namespace internal
253 
254 } // namespace jni
255 } // namespace facebook
256