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,
42 template <const
char* excludeFuncPrefix>
43 WPI_DEPRECATED(
"use StringRef function instead")
44 std::string GetJavaStackTrace(JNIEnv* env, std::string* func) {
45 return GetJavaStackTrace(
47 excludeFuncPrefix ==
nullptr ?
StringRef() : excludeFuncPrefix);
57 JClass(JNIEnv* env,
const char* name) {
58 jclass local = env->FindClass(name);
60 m_cls =
static_cast<jclass
>(env->NewGlobalRef(local));
61 env->DeleteLocalRef(local);
64 void free(JNIEnv* env) {
65 if (m_cls) env->DeleteGlobalRef(m_cls);
69 explicit operator bool()
const {
return m_cls; }
71 operator jclass()
const {
return m_cls; }
74 jclass m_cls =
nullptr;
88 m_cls =
static_cast<T
>(env->NewGlobalRef(obj));
91 void free(JNIEnv* env) {
92 if (m_cls) env->DeleteGlobalRef(m_cls);
96 explicit operator bool()
const {
return m_cls; }
98 operator T()
const {
return m_cls; }
106 template <
typename T>
109 JLocal(JNIEnv* env, T obj) : m_env(env), m_obj(obj) {}
111 JLocal(
JLocal&& oth) : m_env(oth.m_env), m_obj(oth.m_obj) {
122 if (m_obj) m_env->DeleteLocalRef(m_obj);
124 operator T() {
return m_obj; }
125 T obj() {
return m_obj; }
143 jsize size = env->GetStringLength(str);
144 const jchar* chars = env->GetStringCritical(str,
nullptr);
147 env->ReleaseStringCritical(str, chars);
150 errs() <<
"JStringRef was passed a null pointer at \n"
151 << GetJavaStackTrace(env);
154 m_str.push_back(
'\0');
158 operator StringRef()
const {
return m_str; }
160 const char* c_str()
const {
return m_str.
data(); }
161 size_t size()
const {
return m_str.size(); }
170 template <
typename C,
typename T>
174 template <
typename C>
177 operator StringRef()
const {
return str(); }
180 auto arr =
static_cast<const C*
>(
this)->array();
182 return StringRef{
reinterpret_cast<const char*
>(arr.data()), arr.
size()};
187 template <
typename T>
190 explicit operator bool()
const {
return this->m_elements !=
nullptr; }
196 return ArrayRef<T>{this->m_elements, this->m_size};
206 m_elements(oth.m_elements) {
207 oth.m_jarr =
nullptr;
208 oth.m_elements =
nullptr;
212 this->m_env = oth.m_env;
213 this->m_jarr = oth.m_jarr;
214 this->m_size = oth.m_size;
215 this->m_elements = oth.m_elements;
216 oth.m_jarr =
nullptr;
217 oth.m_elements =
nullptr;
224 this->m_jarr =
nullptr;
226 this->m_elements = elements;
233 this->m_elements =
nullptr;
237 :
JArrayRefBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {}
240 jarray m_jarr =
nullptr;
249 #define WPI_JNI_JARRAYREF(T, F) \
250 class J##F##ArrayRef : public detail::JArrayRefBase<T> { \
252 J##F##ArrayRef(JNIEnv* env, jobject bb, int len) \
253 : detail::JArrayRefBase<T>( \
255 static_cast<T*>(bb ? env->GetDirectBufferAddress(bb) : nullptr), \
258 errs() << "JArrayRef was passed a null pointer at \n" \
259 << GetJavaStackTrace(env); \
261 J##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \
262 : detail::JArrayRefBase<T>(env, jarr, len) { \
264 m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
266 errs() << "JArrayRef was passed a null pointer at \n" \
267 << GetJavaStackTrace(env); \
269 J##F##ArrayRef(JNIEnv* env, T##Array jarr) \
270 : detail::JArrayRefBase<T>(env, jarr) { \
272 m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
274 errs() << "JArrayRef was passed a null pointer at \n" \
275 << GetJavaStackTrace(env); \
277 ~J##F##ArrayRef() { \
278 if (m_jarr && m_elements) \
279 m_env->Release##F##ArrayElements(static_cast<T##Array>(m_jarr), \
280 m_elements, JNI_ABORT); \
284 class CriticalJ##F##ArrayRef : public detail::JArrayRefBase<T> { \
286 CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \
287 : detail::JArrayRefBase<T>(env, jarr, len) { \
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(JNIEnv* env, T##Array jarr) \
296 : detail::JArrayRefBase<T>(env, jarr) { \
299 static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); \
301 errs() << "JArrayRef was passed a null pointer at \n" \
302 << GetJavaStackTrace(env); \
304 ~CriticalJ##F##ArrayRef() { \
305 if (m_jarr && m_elements) \
306 m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, JNI_ABORT); \
310 WPI_JNI_JARRAYREF(jboolean, Boolean)
311 WPI_JNI_JARRAYREF(jbyte, Byte)
312 WPI_JNI_JARRAYREF(jshort, Short)
313 WPI_JNI_JARRAYREF(jint, Int)
314 WPI_JNI_JARRAYREF(jlong, Long)
315 WPI_JNI_JARRAYREF(jfloat, Float)
316 WPI_JNI_JARRAYREF(jdouble, Double)
318 #undef WPI_JNI_JARRAYREF
325 inline jstring MakeJString(JNIEnv* env,
StringRef str) {
328 return env->NewString(chars.begin(), chars.size());
337 template <
typename T,
338 bool = (std::is_integral<T>::value &&
sizeof(jint) ==
sizeof(T))>
340 static jintArray ToJava(JNIEnv* env,
ArrayRef<T> arr) {
341 jintArray jarr = env->NewIntArray(arr.
size());
342 if (!jarr)
return nullptr;
344 static_cast<jint*
>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
345 if (!elements)
return nullptr;
346 for (
size_t i = 0; i < arr.
size(); ++i)
347 elements[i] = static_cast<jint>(arr[i]);
348 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
354 template <
typename T>
356 static jintArray ToJava(JNIEnv* env,
ArrayRef<T> arr) {
357 jintArray jarr = env->NewIntArray(arr.
size());
358 if (!jarr)
return nullptr;
359 env->SetIntArrayRegion(jarr, 0, arr.
size(),
360 reinterpret_cast<const jint*
>(arr.data()));
368 template <
typename T>
369 inline jintArray MakeJIntArray(JNIEnv* env,
ArrayRef<T> arr) {
375 template <
typename T>
377 return detail::ConvertIntArray<T>::ToJava(env, arr);
382 template <
typename T>
383 inline jintArray MakeJIntArray(JNIEnv* env,
const std::vector<T>& arr) {
384 return detail::ConvertIntArray<T>::ToJava(env, arr);
388 inline jbyteArray MakeJByteArray(JNIEnv* env, StringRef str) {
389 jbyteArray jarr = env->NewByteArray(str.size());
390 if (!jarr)
return nullptr;
391 env->SetByteArrayRegion(jarr, 0, str.size(),
392 reinterpret_cast<const jbyte*
>(str.data()));
397 inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<int> arr) {
398 jbooleanArray jarr = env->NewBooleanArray(arr.size());
399 if (!jarr)
return nullptr;
401 static_cast<jboolean*
>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
402 if (!elements)
return nullptr;
403 for (
size_t i = 0; i < arr.size(); ++i)
404 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
405 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
410 inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<bool> arr) {
411 jbooleanArray jarr = env->NewBooleanArray(arr.size());
412 if (!jarr)
return nullptr;
414 static_cast<jboolean*
>(env->GetPrimitiveArrayCritical(jarr,
nullptr));
415 if (!elements)
return nullptr;
416 for (
size_t i = 0; i < arr.size(); ++i)
417 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
418 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
424 #define WPI_JNI_MAKEJARRAY(T, F) \
425 inline T##Array MakeJ##F##Array(JNIEnv* env, ArrayRef<T> arr) { \
426 T##Array jarr = env->New##F##Array(arr.size()); \
427 if (!jarr) return nullptr; \
428 env->Set##F##ArrayRegion(jarr, 0, arr.size(), arr.data()); \
432 WPI_JNI_MAKEJARRAY(jboolean, Boolean)
433 WPI_JNI_MAKEJARRAY(jbyte, Byte)
434 WPI_JNI_MAKEJARRAY(jshort, Short)
435 WPI_JNI_MAKEJARRAY(jlong, Long)
436 WPI_JNI_MAKEJARRAY(jfloat, Float)
437 WPI_JNI_MAKEJARRAY(jdouble, Double)
439 #undef WPI_JNI_MAKEJARRAY
442 inline jobjectArray MakeJStringArray(JNIEnv* env, ArrayRef<std::string> arr) {
443 static JClass stringCls{env,
"java/lang/String"};
444 if (!stringCls)
return nullptr;
445 jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls,
nullptr);
446 if (!jarr)
return nullptr;
447 for (
size_t i = 0; i < arr.size(); ++i) {
448 JLocal<jstring> elem{env, MakeJString(env, arr[i])};
449 env->SetObjectArrayElement(jarr, i, elem.obj());
469 template <
typename T>
474 std::queue<T> m_queue;
475 jobject m_func =
nullptr;
479 template <
typename T>
482 void SetFunc(JNIEnv* env, jobject func, jmethodID mid);
484 template <
typename... Args>
485 void Send(Args&&... args);
488 template <
typename T>
490 auto thr = this->GetThread();
493 if (thr->m_func) env->DeleteGlobalRef(thr->m_func);
495 thr->m_func = env->NewGlobalRef(func);
499 template <
typename T>
500 template <
typename... Args>
501 void JCallbackManager<T>::Send(Args&&... args) {
502 auto thr = this->GetThread();
504 thr->m_queue.emplace(std::forward<Args>(args)...);
505 thr->m_cond.notify_one();
508 template <
typename T>
509 void JCallbackThread<T>::Main() {
511 JavaVMAttachArgs args;
512 args.version = JNI_VERSION_1_2;
513 args.name =
const_cast<char*
>(T::GetName());
514 args.group =
nullptr;
515 jint rs = T::GetJVM()->AttachCurrentThreadAsDaemon(
516 reinterpret_cast<void**>(&env), &args);
517 if (rs != JNI_OK)
return;
519 std::unique_lock<wpi::mutex> lock(m_mutex);
521 m_cond.wait(lock, [&] {
return !(m_active && m_queue.empty()); });
522 if (!m_active)
break;
523 while (!m_queue.empty()) {
524 if (!m_active)
break;
525 auto item = std::move(m_queue.front());
530 item.CallJava(env, func, mid);
531 if (env->ExceptionCheck()) {
532 env->ExceptionDescribe();
533 env->ExceptionClear();
539 JavaVM* jvm = T::GetJVM();
540 if (jvm) jvm->DetachCurrentThread();
543 template <
typename T>
552 inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func,
555 static JClass throwableCls(env,
"java/lang/Throwable");
556 if (!throwableCls)
return "";
557 static jmethodID constructorId =
nullptr;
559 constructorId = env->GetMethodID(throwableCls,
"<init>",
"()V");
560 JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
565 static jmethodID getStackTraceId =
nullptr;
566 if (!getStackTraceId)
567 getStackTraceId = env->GetMethodID(throwableCls,
"getStackTrace",
568 "()[Ljava/lang/StackTraceElement;");
572 env, static_cast<jobjectArray>(
573 env->CallObjectMethod(throwable, getStackTraceId)));
575 if (!stackTrace)
return "";
578 jsize stackTraceLength = env->GetArrayLength(stackTrace);
581 static JClass stackTraceElementCls(env,
"java/lang/StackTraceElement");
582 if (!stackTraceElementCls)
return "";
583 static jmethodID toStringId =
nullptr;
585 toStringId = env->GetMethodID(stackTraceElementCls,
"toString",
586 "()Ljava/lang/String;");
588 bool haveLoc =
false;
591 for (jsize i = 0; i < stackTraceLength; i++) {
594 env, env->GetObjectArrayElement(stackTrace, i));
598 env, static_cast<jstring>(
599 env->CallObjectMethod(curStackTraceElement, toStringId)));
601 if (!stackElementString)
return "";
612 }
else if (i > 1 && !haveLoc && !excludeFuncPrefix.
empty() &&
613 !elem.str().startswith(excludeFuncPrefix)) {
633 env->GetMethodID(m_cls,
"<init>",
"(Ljava/lang/String;)V");
636 void Throw(JNIEnv* env, jstring msg) {
637 jobject exception = env->NewObject(m_cls, m_constructor, msg);
638 env->Throw(static_cast<jthrowable>(exception));
641 void Throw(JNIEnv* env,
StringRef msg) { Throw(env, MakeJString(env, msg)); }
643 explicit operator bool()
const {
return m_constructor; }
646 jmethodID m_constructor =
nullptr;
657 #endif // WPIUTIL_WPI_JNI_UTIL_H_
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: hostname.h:17
Definition: SafeThread.h:20
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small...
Definition: SmallVector.h:868
Definition: jni_util.h:188
Definition: jni_util.h:649
Definition: jni_util.h:171
Definition: jni_util.h:53
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory)...
Definition: ArrayRef.h:41
Definition: jni_util.h:355
Definition: SafeThread.h:120
WPILib C++ utilities (wpiutil) namespace.
Definition: SmallString.h:21
Definition: jni_util.h:83
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:482
ArrayRef< T > makeArrayRef(const T &OneElt)
Construct an ArrayRef from a single element.
Definition: ArrayRef.h:451
Definition: jni_util.h:470
bool convertUTF8ToUTF16String(StringRef SrcUTF8, SmallVectorImpl< UTF16 > &DstUTF16)
Converts a UTF-8 string into a UTF-16 string with native endianness.
Definition: jni_util.h:339
Definition: jni_util.h:544
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE bool empty() const noexcept
empty - Check if the string is empty.
Definition: StringRef.h:133
bool convertUTF16ToUTF8String(ArrayRef< UTF16 > SrcUTF16, SmallVectorImpl< char > &DstUTF8)
Converts a UTF-16 string into a UTF-8 string.
auto size(R &&Range, typename std::enable_if< std::is_same< typename std::iterator_traits< decltype(Range.begin())>::iterator_category, std::random_access_iterator_tag >::value, void >::type *=nullptr) -> decltype(std::distance(Range.begin(), Range.end()))
Get the size of a range.
Definition: STLExtras.h:999
Definition: jni_util.h:139
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:49
Definition: jni_util.h:77
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE size_t size() const noexcept
size - Get the string size.
Definition: StringRef.h:138
Definition: jni_util.h:107
raw_ostream & errs()
This returns a reference to a raw_ostream for standard error.
Definition: jni_util.h:480
pointer data()
Return a pointer to the vector's buffer, even if empty().
Definition: SmallVector.h:151
Definition: jni_util.h:627
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:149