8 #ifndef WPIUTIL_WPI_JNI_UTIL_H_
9 #define WPIUTIL_WPI_JNI_UTIL_H_
15 #include <type_traits>
19 #include "wpi/ArrayRef.h"
20 #include "wpi/ConvertUTF.h"
21 #include "wpi/SafeThread.h"
22 #include "wpi/SmallString.h"
23 #include "wpi/SmallVector.h"
24 #include "wpi/StringRef.h"
25 #include "wpi/deprecated.h"
26 #include "wpi/mutex.h"
27 #include "wpi/raw_ostream.h"
38 std::string GetJavaStackTrace(JNIEnv* env, std::string* func =
nullptr,
48 JClass(JNIEnv* env,
const char* name) {
49 jclass local = env->FindClass(name);
51 m_cls = static_cast<jclass>(env->NewGlobalRef(local));
52 env->DeleteLocalRef(local);
55 void free(JNIEnv* env) {
56 if (m_cls) env->DeleteGlobalRef(m_cls);
60 explicit operator bool()
const {
return m_cls; }
62 operator jclass()
const {
return m_cls; }
65 jclass m_cls =
nullptr;
79 m_cls = static_cast<T>(env->NewGlobalRef(obj));
82 void free(JNIEnv* env) {
83 if (m_cls) env->DeleteGlobalRef(m_cls);
87 explicit operator bool()
const {
return m_cls; }
89 operator T()
const {
return m_cls; }
100 JLocal(JNIEnv* env, T obj) : m_env(env), m_obj(obj) {}
102 JLocal(
JLocal&& oth) : m_env(oth.m_env), m_obj(oth.m_obj) {
113 if (m_obj) m_env->DeleteLocalRef(m_obj);
115 operator T() {
return m_obj; }
116 T obj() {
return m_obj; }
134 jsize size = env->GetStringLength(str);
135 const jchar* chars = env->GetStringCritical(str,
nullptr);
138 env->ReleaseStringCritical(str, chars);
141 errs() <<
"JStringRef was passed a null pointer at \n"
142 << GetJavaStackTrace(env);
145 m_str.push_back(
'\0');
149 operator StringRef()
const {
return m_str; }
151 const char* c_str()
const {
return m_str.
data(); }
152 size_t size()
const {
return m_str.size(); }
161 template <
typename C,
typename T>
165 template <
typename C>
168 operator StringRef()
const {
return str(); }
171 auto arr = static_cast<const C*>(
this)->array();
173 return StringRef{reinterpret_cast<const char*>(arr.data()), arr.
size()};
178 template <
typename T>
181 explicit operator bool()
const {
return this->m_elements !=
nullptr; }
187 return ArrayRef<T>{this->m_elements, this->m_size};
197 m_elements(oth.m_elements) {
198 oth.m_jarr =
nullptr;
199 oth.m_elements =
nullptr;
203 this->m_env = oth.m_env;
204 this->m_jarr = oth.m_jarr;
205 this->m_size = oth.m_size;
206 this->m_elements = oth.m_elements;
207 oth.m_jarr =
nullptr;
208 oth.m_elements =
nullptr;
215 this->m_jarr =
nullptr;
217 this->m_elements = elements;
224 this->m_elements =
nullptr;
228 :
JArrayRefBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {}
231 jarray m_jarr =
nullptr;
240 #define WPI_JNI_JARRAYREF(T, F) \
241 class J##F##ArrayRef : public detail::JArrayRefBase<T> { \
243 J##F##ArrayRef(JNIEnv* env, jobject bb, int len) \
244 : detail::JArrayRefBase<T>( \
246 static_cast<T*>(bb ? env->GetDirectBufferAddress(bb) : nullptr), \
249 errs() << "JArrayRef was passed a null pointer at \n" \
250 << GetJavaStackTrace(env); \
252 J##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \
253 : detail::JArrayRefBase<T>(env, jarr, len) { \
255 m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
257 errs() << "JArrayRef was passed a null pointer at \n" \
258 << GetJavaStackTrace(env); \
260 J##F##ArrayRef(JNIEnv* env, T##Array jarr) \
261 : detail::JArrayRefBase<T>(env, jarr) { \
263 m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
265 errs() << "JArrayRef was passed a null pointer at \n" \
266 << GetJavaStackTrace(env); \
268 ~J##F##ArrayRef() { \
269 if (m_jarr && m_elements) \
270 m_env->Release##F##ArrayElements(static_cast<T##Array>(m_jarr), \
271 m_elements, JNI_ABORT); \
275 class CriticalJ##F##ArrayRef : public detail::JArrayRefBase<T> { \
277 CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \
278 : detail::JArrayRefBase<T>(env, jarr, len) { \
281 static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); \
283 errs() << "JArrayRef was passed a null pointer at \n" \
284 << GetJavaStackTrace(env); \
286 CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr) \
287 : detail::JArrayRefBase<T>(env, jarr) { \
290 static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); \
292 errs() << "JArrayRef was passed a null pointer at \n" \
293 << GetJavaStackTrace(env); \
295 ~CriticalJ##F##ArrayRef() { \
296 if (m_jarr && m_elements) \
297 m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, JNI_ABORT); \
301 WPI_JNI_JARRAYREF(jboolean, Boolean)
302 WPI_JNI_JARRAYREF(jbyte, Byte)
303 WPI_JNI_JARRAYREF(jshort, Short)
304 WPI_JNI_JARRAYREF(jint, Int)
305 WPI_JNI_JARRAYREF(jlong, Long)
306 WPI_JNI_JARRAYREF(jfloat, Float)
307 WPI_JNI_JARRAYREF(jdouble, Double)
309 #undef WPI_JNI_JARRAYREF
316 inline jstring MakeJString(JNIEnv* env,
StringRef str) {
319 return env->NewString(chars.begin(), chars.size());
328 template <
typename T,
329 bool = (std::is_integral<T>::value &&
sizeof(jint) ==
sizeof(T))>
331 static jintArray ToJava(JNIEnv* env,
ArrayRef<T> arr) {
332 jintArray jarr = env->NewIntArray(arr.
size());
333 if (!jarr)
return nullptr;
335 static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
336 if (!elements)
return nullptr;
337 for (
size_t i = 0; i < arr.
size(); ++i)
338 elements[i] = static_cast<jint>(arr[i]);
339 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
345 template <
typename T>
347 static jintArray ToJava(JNIEnv* env,
ArrayRef<T> arr) {
348 jintArray jarr = env->NewIntArray(arr.
size());
349 if (!jarr)
return nullptr;
350 env->SetIntArrayRegion(jarr, 0, arr.
size(),
351 reinterpret_cast<const jint*>(arr.data()));
359 template <
typename T>
360 inline jintArray MakeJIntArray(JNIEnv* env,
ArrayRef<T> arr) {
366 template <
typename T>
368 return detail::ConvertIntArray<T>::ToJava(env, arr);
373 template <
typename T>
374 inline jintArray MakeJIntArray(JNIEnv* env,
const std::vector<T>& arr) {
375 return detail::ConvertIntArray<T>::ToJava(env, arr);
379 inline jbyteArray MakeJByteArray(JNIEnv* env, StringRef str) {
380 jbyteArray jarr = env->NewByteArray(str.size());
381 if (!jarr)
return nullptr;
382 env->SetByteArrayRegion(jarr, 0, str.size(),
383 reinterpret_cast<const jbyte*>(str.data()));
388 inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<int> arr) {
389 jbooleanArray jarr = env->NewBooleanArray(arr.size());
390 if (!jarr)
return nullptr;
392 static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
393 if (!elements)
return nullptr;
394 for (
size_t i = 0; i < arr.size(); ++i)
395 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
396 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
401 inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<bool> arr) {
402 jbooleanArray jarr = env->NewBooleanArray(arr.size());
403 if (!jarr)
return nullptr;
405 static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
406 if (!elements)
return nullptr;
407 for (
size_t i = 0; i < arr.size(); ++i)
408 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
409 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
415 #define WPI_JNI_MAKEJARRAY(T, F) \
416 inline T##Array MakeJ##F##Array(JNIEnv* env, ArrayRef<T> arr) { \
417 T##Array jarr = env->New##F##Array(arr.size()); \
418 if (!jarr) return nullptr; \
419 env->Set##F##ArrayRegion(jarr, 0, arr.size(), arr.data()); \
423 WPI_JNI_MAKEJARRAY(jboolean, Boolean)
424 WPI_JNI_MAKEJARRAY(jbyte, Byte)
425 WPI_JNI_MAKEJARRAY(jshort, Short)
426 WPI_JNI_MAKEJARRAY(jlong, Long)
427 WPI_JNI_MAKEJARRAY(jfloat, Float)
428 WPI_JNI_MAKEJARRAY(jdouble, Double)
430 #undef WPI_JNI_MAKEJARRAY
433 inline jobjectArray MakeJStringArray(JNIEnv* env, ArrayRef<std::string> arr) {
434 static JClass stringCls{env,
"java/lang/String"};
435 if (!stringCls)
return nullptr;
436 jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls,
nullptr);
437 if (!jarr)
return nullptr;
438 for (
size_t i = 0; i < arr.size(); ++i) {
439 JLocal<jstring> elem{env, MakeJString(env, arr[i])};
440 env->SetObjectArrayElement(jarr, i, elem.obj());
460 template <
typename T>
465 std::queue<T> m_queue;
466 jobject m_func =
nullptr;
470 template <
typename T>
474 void SetFunc(JNIEnv* env, jobject func, jmethodID mid);
476 template <
typename... Args>
477 void Send(Args&&... args);
480 template <
typename T>
482 auto thr = this->GetThread();
485 if (thr->m_func) env->DeleteGlobalRef(thr->m_func);
487 thr->m_func = env->NewGlobalRef(func);
491 template <
typename T>
492 template <
typename... Args>
493 void JCallbackManager<T>::Send(Args&&... args) {
494 auto thr = this->GetThread();
496 thr->m_queue.emplace(std::forward<Args>(args)...);
497 thr->m_cond.notify_one();
500 template <
typename T>
501 void JCallbackThread<T>::Main() {
503 JavaVMAttachArgs args;
504 args.version = JNI_VERSION_1_2;
505 args.name = const_cast<char*>(T::GetName());
506 args.group =
nullptr;
507 jint rs = T::GetJVM()->AttachCurrentThreadAsDaemon(
508 reinterpret_cast<void**>(&env), &args);
509 if (rs != JNI_OK)
return;
511 std::unique_lock lock(m_mutex);
513 m_cond.wait(lock, [&] {
return !(m_active && m_queue.empty()); });
514 if (!m_active)
break;
515 while (!m_queue.empty()) {
516 if (!m_active)
break;
517 auto item = std::move(m_queue.front());
522 item.CallJava(env, func, mid);
523 if (env->ExceptionCheck()) {
524 env->ExceptionDescribe();
525 env->ExceptionClear();
531 JavaVM* jvm = T::GetJVM();
532 if (jvm) jvm->DetachCurrentThread();
535 template <
typename T>
544 inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func,
547 static JClass throwableCls(env,
"java/lang/Throwable");
548 if (!throwableCls)
return "";
549 static jmethodID constructorId =
nullptr;
551 constructorId = env->GetMethodID(throwableCls,
"<init>",
"()V");
552 JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
557 static jmethodID getStackTraceId =
nullptr;
558 if (!getStackTraceId)
559 getStackTraceId = env->GetMethodID(throwableCls,
"getStackTrace",
560 "()[Ljava/lang/StackTraceElement;");
564 env, static_cast<jobjectArray>(
565 env->CallObjectMethod(throwable, getStackTraceId)));
567 if (!stackTrace)
return "";
570 jsize stackTraceLength = env->GetArrayLength(stackTrace);
573 static JClass stackTraceElementCls(env,
"java/lang/StackTraceElement");
574 if (!stackTraceElementCls)
return "";
575 static jmethodID toStringId =
nullptr;
577 toStringId = env->GetMethodID(stackTraceElementCls,
"toString",
578 "()Ljava/lang/String;");
580 bool haveLoc =
false;
583 for (jsize i = 0; i < stackTraceLength; i++) {
586 env, env->GetObjectArrayElement(stackTrace, i));
590 env, static_cast<jstring>(
591 env->CallObjectMethod(curStackTraceElement, toStringId)));
593 if (!stackElementString)
return "";
604 }
else if (i > 1 && !haveLoc && !excludeFuncPrefix.
empty() &&
605 !elem.str().startswith(excludeFuncPrefix)) {
625 env->GetMethodID(m_cls,
"<init>",
"(Ljava/lang/String;)V");
628 void Throw(JNIEnv* env, jstring msg) {
629 jobject exception = env->NewObject(m_cls, m_constructor, msg);
630 env->Throw(static_cast<jthrowable>(exception));
633 void Throw(JNIEnv* env,
StringRef msg) { Throw(env, MakeJString(env, msg)); }
635 explicit operator bool()
const {
return m_constructor; }
638 jmethodID m_constructor =
nullptr;
649 #endif // WPIUTIL_WPI_JNI_UTIL_H_