WPILibC++ 2025.3.1
Loading...
Searching...
No Matches
jni_util.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#ifndef WPIUTIL_WPI_JNI_UTIL_H_
6#define WPIUTIL_WPI_JNI_UTIL_H_
7
8#include <jni.h>
9
10#include <concepts>
11#include <queue>
12#include <span>
13#include <string>
14#include <string_view>
15#include <utility>
16#include <vector>
17
18#include "wpi/ConvertUTF.h"
19#include "wpi/SafeThread.h"
20#include "wpi/SmallString.h"
21#include "wpi/SmallVector.h"
22#include "wpi/StringExtras.h"
23#include "wpi/mutex.h"
24#include "wpi/print.h"
25#include "wpi/raw_ostream.h"
26#include "wpi/string.h"
27
28/** Java Native Interface (JNI) utility functions */
29namespace wpi::java {
30
31/**
32 * Gets a Java stack trace.
33 *
34 * Also provides the last function in the stack trace not starting with
35 * excludeFuncPrefix (useful for e.g. finding the first user call to a series of
36 * library functions).
37 *
38 * @param env JRE environment.
39 * @param func Storage for last function in the stack trace not starting with
40 * excludeFuncPrefix.
41 * @param excludeFuncPrefix Prefix for functions to ignore in stack trace.
42 */
43std::string GetJavaStackTrace(JNIEnv* env, std::string* func = nullptr,
44 std::string_view excludeFuncPrefix = {});
45
46/**
47 * Finds a class and keeps it as a global reference.
48 *
49 * Use with caution, as the destructor does NOT call DeleteGlobalRef due to
50 * potential shutdown issues with doing so.
51 */
52class JClass {
53 public:
54 JClass() = default;
55
56 JClass(JNIEnv* env, const char* name) {
57 jclass local = env->FindClass(name);
58 if (!local) {
59 return;
60 }
61 m_cls = static_cast<jclass>(env->NewGlobalRef(local));
62 env->DeleteLocalRef(local);
63 }
64
65 void free(JNIEnv* env) {
66 if (m_cls) {
67 env->DeleteGlobalRef(m_cls);
68 }
69 m_cls = nullptr;
70 }
71
72 explicit operator bool() const { return m_cls; }
73
74 operator jclass() const { return m_cls; }
75
76 protected:
77 jclass m_cls = nullptr;
78};
79
80struct JClassInit {
81 const char* name;
83};
84
85template <typename T>
86class JGlobal {
87 public:
88 JGlobal() = default;
89
90 JGlobal(JNIEnv* env, T obj) {
91 m_cls = static_cast<T>(env->NewGlobalRef(obj));
92 }
93
94 void free(JNIEnv* env) {
95 if (m_cls) {
96 env->DeleteGlobalRef(m_cls);
97 }
98 m_cls = nullptr;
99 }
100
101 explicit operator bool() const { return m_cls; }
102
103 operator T() const { return m_cls; } // NOLINT
104
105 protected:
106 T m_cls = nullptr;
107};
108
109/**
110 * Container class for cleaning up Java local references.
111 *
112 * The destructor calls DeleteLocalRef.
113 */
114template <typename T>
115class JLocal {
116 public:
117 JLocal(JNIEnv* env, T obj) : m_env(env), m_obj(obj) {}
118 JLocal(const JLocal&) = delete;
119 JLocal(JLocal&& oth) : m_env(oth.m_env), m_obj(oth.m_obj) {
120 oth.m_obj = nullptr;
121 }
122 JLocal& operator=(const JLocal&) = delete;
124 m_env = oth.m_env;
125 m_obj = oth.m_obj;
126 oth.m_obj = nullptr;
127 return *this;
128 }
130 if (m_obj) {
131 m_env->DeleteLocalRef(m_obj);
132 }
133 }
134 operator T() { return m_obj; } // NOLINT
135 T obj() { return m_obj; }
136
137 private:
138 JNIEnv* m_env;
139 T m_obj;
140};
141
142//
143// Conversions from Java objects to C++
144//
145
146/**
147 * Java string (jstring) reference.
148 *
149 * The string is provided as UTF8. This is not actually a reference, as it makes
150 * a copy of the string characters, but it's named this way for consistency.
151 */
153 public:
154 JStringRef(JNIEnv* env, jstring str) {
155 if (str) {
156 jsize size = env->GetStringLength(str);
157 const jchar* chars = env->GetStringCritical(str, nullptr);
158 if (chars) {
159 convertUTF16ToUTF8String(std::span<const jchar>(chars, size), m_str);
160 env->ReleaseStringCritical(str, chars);
161 }
162 } else {
163 wpi::print(stderr, "JStringRef was passed a null pointer at\n",
164 GetJavaStackTrace(env));
165 }
166 // Ensure str is null-terminated.
167 m_str.push_back('\0');
168 m_str.pop_back();
169 }
170
171 operator std::string_view() const { return m_str.str(); } // NOLINT
172 std::string_view str() const { return m_str.str(); }
173 const char* c_str() const { return m_str.data(); }
174 size_t size() const { return m_str.size(); }
175 WPI_String wpi_str() const { return wpi::make_string(str()); }
176
177 private:
178 SmallString<128> m_str;
179};
180
181namespace detail {
182
183template <typename T>
184struct ArrayHelper {};
185
186#define WPI_JNI_ARRAYHELPER(T, F) \
187 template <> \
188 struct ArrayHelper<T> { \
189 using jarray_type = T##Array; \
190 static T* GetArrayElements(JNIEnv* env, jarray_type jarr) { \
191 return env->Get##F##ArrayElements(jarr, nullptr); \
192 } \
193 static void ReleaseArrayElements(JNIEnv* env, jarray_type jarr, T* elems, \
194 jint mode) { \
195 env->Release##F##ArrayElements(jarr, elems, mode); \
196 } \
197 };
198
200WPI_JNI_ARRAYHELPER(jbyte, Byte)
201WPI_JNI_ARRAYHELPER(jshort, Short)
202WPI_JNI_ARRAYHELPER(jint, Int)
203WPI_JNI_ARRAYHELPER(jlong, Long)
204WPI_JNI_ARRAYHELPER(jfloat, Float)
205WPI_JNI_ARRAYHELPER(jdouble, Double)
206
207#undef WPI_JNI_ARRAYHELPER
208
209template <typename T>
210concept JArrayType =
211 requires { typename ArrayHelper<std::remove_cv_t<T>>::jarray_type; };
212
213template <typename CvSrc, typename Dest>
214struct copy_cv {
215 private:
216 using U0 = std::remove_cv_t<Dest>;
217 using U1 = std::conditional_t<std::is_const_v<CvSrc>, const U0, U0>;
218 using U2 = std::conditional_t<std::is_volatile_v<CvSrc>, volatile U1, U1>;
219
220 public:
221 using type = U2;
222};
223
224template <typename CvSrc, typename Dest>
226
227template <typename From, typename To>
229 !(std::is_const_v<From> && !std::is_const_v<To>) &&
230 !(std::is_volatile_v<From> && !std::is_volatile_v<To>);
231
232/**
233 * Helper class for working with JNI arrays.
234 *
235 * This class exposes an is_valid() member and an explicit conversion to bool
236 * which indicate if the span is valid. Operations on invalid spans are
237 * undefined.
238 *
239 * Note that Set<PrimitiveType>ArrayRegion may be faster for pure writes since
240 * it avoids copying the elements from Java to C++.
241 *
242 * @tparam T The element type of the array (e.g., jdouble).
243 * @tparam IsCritical If true, Get/ReleasePrimitiveArrayCritical will be used
244 * instead of Get/Release<PrimitiveType>ArrayElements.
245 * @tparam Size The number of elements in the span.
246 */
247template <JArrayType T, bool IsCritical, size_t Size = std::dynamic_extent>
250 using jarray_type = typename ArrHelper::jarray_type;
251
252 public:
253 JSpanBase(const JSpanBase&) = delete;
254 JSpanBase& operator=(const JSpanBase&) = delete;
255
257 : m_valid{other.m_valid},
258 m_env{other.m_env},
259 m_jarr{other.m_jarr},
260 m_size{other.m_size},
261 m_elements{other.m_elements} {
262 other.m_jarr = nullptr;
263 other.m_elements = nullptr;
264 }
265
267 m_valid = other.m_valid;
268 m_env = other.m_env;
269 m_jarr = other.m_jarr;
270 m_size = other.m_size;
271 m_elements = other.m_elements;
272 other.m_valid = false;
273 other.m_jarr = nullptr;
274 other.m_elements = nullptr;
275 return *this;
276 }
277
278 JSpanBase(JNIEnv* env, jobject bb, size_t size)
279 requires(!IsCritical)
280 : m_valid{Size == std::dynamic_extent || size == Size},
281 m_env{env},
282 m_jarr{nullptr},
283 m_size{size},
284 m_elements{static_cast<std::remove_cv_t<T>*>(
285 bb ? env->GetDirectBufferAddress(bb) : nullptr)} {
286 if (!bb) {
287 wpi::print(stderr, "JSpan was passed a null pointer at\n",
288 GetJavaStackTrace(env));
289 }
290 }
291
292 JSpanBase(JNIEnv* env, jarray_type jarr, size_t size)
293 : m_valid{Size == std::dynamic_extent || size == Size},
294 m_env{env},
295 m_jarr{jarr},
296 m_size{size},
297 m_elements{nullptr} {
298 if (jarr) {
299 if constexpr (IsCritical) {
300 m_elements = static_cast<std::remove_cv_t<T>*>(
301 env->GetPrimitiveArrayCritical(jarr, nullptr));
302 } else {
303 m_elements = ArrHelper::GetArrayElements(env, jarr);
304 }
305 } else {
306 wpi::print(stderr, "JSpan was passed a null pointer at\n",
307 GetJavaStackTrace(env));
308 }
309 }
310
311 JSpanBase(JNIEnv* env, jarray_type jarr)
312 : JSpanBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {}
313
315 if (m_jarr && m_elements) {
316 constexpr jint mode = std::is_const_v<T> ? JNI_ABORT : 0;
317 if constexpr (IsCritical) {
318 m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, mode);
319 } else {
320 ArrHelper::ReleaseArrayElements(m_env, m_jarr, m_elements, mode);
321 }
322 }
323 }
324
325 operator std::span<T, Size>() const { return array(); }
326
327 std::span<T, Size> array() const {
328 // If Size is dynamic_extent, can return empty span
329 // Unfortunately, sized spans will return a span over nullptr if m_elements
330 // is nullptr
331 if constexpr (Size == std::dynamic_extent) {
332 if (!m_elements) {
333 return {};
334 }
335 }
336 return std::span<T, Size>{m_elements, m_size};
337 }
338
339 T* begin() const { return m_elements; }
340
341 T* end() const { return m_elements + m_size; }
342
343 bool is_valid() const { return m_valid && m_elements != nullptr; }
344
345 explicit operator bool() const { return is_valid(); }
346
347 T* data() const { return m_elements; }
348
349 size_t size() const { return m_size; }
350
351 const T& operator[](size_t i) const { return m_elements[i]; }
352
353 T& operator[](size_t i)
354 requires(!std::is_const_v<T>)
355 {
356 return m_elements[i];
357 }
358
359 // Provide std::string_view and span<const uint8_t> conversions for jbyte
360
361 operator std::string_view() const
362 requires std::is_same_v<std::remove_cv_t<T>, jbyte>
363 {
364 return str();
365 }
366
367 std::string_view str() const
368 requires std::is_same_v<std::remove_cv_t<T>, jbyte>
369 {
370 auto arr = array();
371 if (arr.empty()) {
372 return {};
373 }
374 return {reinterpret_cast<const char*>(arr.data()), arr.size()};
375 }
376
377 std::span<copy_cv_t<T, uint8_t>, Size> uarray() const
378 requires std::is_same_v<std::remove_cv_t<T>, jbyte>
379 {
380 auto arr = array();
381 if (arr.empty()) {
382 return {};
383 }
384 return {reinterpret_cast<const uint8_t*>(arr.data()), arr.size()};
385 }
386
387 // Support both "long long" and "long" on 64-bit systems
388
389 template <typename U>
390 requires(sizeof(U) == sizeof(jlong) && std::integral<U> &&
392 operator std::span<U, Size>() const
393 requires std::is_same_v<std::remove_cv_t<T>, jlong>
394 {
395 auto arr = array();
396 if (arr.empty()) {
397 return {};
398 }
399 return {reinterpret_cast<U*>(arr.data()), arr.size()};
400 }
401
402 // FIXME doxygen gives error parsing initializer list
403 //! @cond Doxygen_Suppress
404 private:
405 bool m_valid;
406 JNIEnv* m_env;
407 jarray_type m_jarr = nullptr;
408 size_t m_size;
409 std::remove_cv_t<T>* m_elements;
410 //! @endcond
411};
412
413} // namespace detail
414
415template <typename T, size_t Extent = std::dynamic_extent>
417
418template <typename T, size_t Extent = std::dynamic_extent>
420
421//
422// Conversions from C++ to Java objects
423//
424
425/**
426 * Convert a UTF8 string into a jstring.
427 *
428 * @param env JRE environment.
429 * @param str String to convert.
430 */
431inline jstring MakeJString(JNIEnv* env, std::string_view str) {
433 convertUTF8ToUTF16String(str, chars);
434 return env->NewString(chars.begin(), chars.size());
435}
436
437// details for MakeJIntArray
438namespace detail {
439
440template <typename T>
442 static jintArray ToJava(JNIEnv* env, std::span<const T> arr) {
443 if constexpr (sizeof(T) == sizeof(jint) && std::integral<T>) {
444 // Fast path (use SetIntArrayRegion).
445 jintArray jarr = env->NewIntArray(arr.size());
446 if (!jarr) {
447 return nullptr;
448 }
449 env->SetIntArrayRegion(jarr, 0, arr.size(),
450 reinterpret_cast<const jint*>(arr.data()));
451 return jarr;
452 } else {
453 // Slow path (get primitive array and set individual elements).
454 //
455 // This is used if the input type is not an integer of the same size (note
456 // signed/unsigned is ignored).
457 jintArray jarr = env->NewIntArray(arr.size());
458 if (!jarr) {
459 return nullptr;
460 }
461 jint* elements =
462 static_cast<jint*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
463 if (!elements) {
464 return nullptr;
465 }
466 for (size_t i = 0; i < arr.size(); ++i) {
467 elements[i] = static_cast<jint>(arr[i]);
468 }
469 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
470 return jarr;
471 }
472 }
473};
474
475} // namespace detail
476
477/**
478 * Convert a span to a jintArray.
479 *
480 * @param env JRE environment.
481 * @param arr Span to convert.
482 */
483template <typename T>
484inline jintArray MakeJIntArray(JNIEnv* env, std::span<const T> arr) {
485 return detail::ConvertIntArray<T>::ToJava(env, arr);
486}
487
488/**
489 * Convert a span to a jintArray.
490 *
491 * @param env JRE environment.
492 * @param arr Span to convert.
493 */
494template <typename T>
495inline jintArray MakeJIntArray(JNIEnv* env, std::span<T> arr) {
496 return detail::ConvertIntArray<T>::ToJava(env, arr);
497}
498
499/**
500 * Convert a SmallVector to a jintArray.
501 *
502 * This is required in addition to ArrayRef because template resolution occurs
503 * prior to implicit conversions.
504 *
505 * @param env JRE environment.
506 * @param arr SmallVector to convert.
507 */
508template <typename T>
509inline jintArray MakeJIntArray(JNIEnv* env, const SmallVectorImpl<T>& arr) {
510 return detail::ConvertIntArray<T>::ToJava(env, arr);
511}
512
513/**
514 * Convert a std::vector to a jintArray.
515 *
516 * This is required in addition to ArrayRef because template resolution occurs
517 * prior to implicit conversions.
518 *
519 * @param env JRE environment.
520 * @param arr SmallVector to convert.
521 */
522template <typename T>
523inline jintArray MakeJIntArray(JNIEnv* env, const std::vector<T>& arr) {
524 return detail::ConvertIntArray<T>::ToJava(env, arr);
525}
526
527/**
528 * Convert a span into a jbyteArray.
529 *
530 * @param env JRE environment.
531 * @param str span to convert.
532 */
533inline jbyteArray MakeJByteArray(JNIEnv* env, std::span<const uint8_t> str) {
534 jbyteArray jarr = env->NewByteArray(str.size());
535 if (!jarr) {
536 return nullptr;
537 }
538 env->SetByteArrayRegion(jarr, 0, str.size(),
539 reinterpret_cast<const jbyte*>(str.data()));
540 return jarr;
541}
542
543/**
544 * Convert an array of integers into a jbooleanArray.
545 *
546 * @param env JRE environment.
547 * @param arr Array to convert.
548 */
549inline jbooleanArray MakeJBooleanArray(JNIEnv* env, std::span<const int> arr) {
550 jbooleanArray jarr = env->NewBooleanArray(arr.size());
551 if (!jarr) {
552 return nullptr;
553 }
554 jboolean* elements =
555 static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
556 if (!elements) {
557 return nullptr;
558 }
559 for (size_t i = 0; i < arr.size(); ++i) {
560 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
561 }
562 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
563 return jarr;
564}
565
566/**
567 * Convert an array of booleans into a jbooleanArray.
568 *
569 * @param env JRE environment.
570 * @param arr Array to convert.
571 */
572inline jbooleanArray MakeJBooleanArray(JNIEnv* env, std::span<const bool> arr) {
573 jbooleanArray jarr = env->NewBooleanArray(arr.size());
574 if (!jarr) {
575 return nullptr;
576 }
577 jboolean* elements =
578 static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
579 if (!elements) {
580 return nullptr;
581 }
582 for (size_t i = 0; i < arr.size(); ++i) {
583 elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
584 }
585 env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
586 return jarr;
587}
588
589// Other MakeJ*Array conversions.
590
591#define WPI_JNI_MAKEJARRAY(T, F) \
592 inline T##Array MakeJ##F##Array(JNIEnv* env, std::span<const T> arr) { \
593 T##Array jarr = env->New##F##Array(arr.size()); \
594 if (!jarr) { \
595 return nullptr; \
596 } \
597 env->Set##F##ArrayRegion(jarr, 0, arr.size(), arr.data()); \
598 return jarr; \
599 }
600
602WPI_JNI_MAKEJARRAY(jbyte, Byte)
603WPI_JNI_MAKEJARRAY(jshort, Short)
604WPI_JNI_MAKEJARRAY(jfloat, Float)
605WPI_JNI_MAKEJARRAY(jdouble, Double)
606
607#undef WPI_JNI_MAKEJARRAY
608
609template <class T>
610 requires(sizeof(typename T::value_type) == sizeof(jlong) &&
611 std::integral<typename T::value_type>)
612inline jlongArray MakeJLongArray(JNIEnv* env, const T& arr) {
613 jlongArray jarr = env->NewLongArray(arr.size());
614 if (!jarr) {
615 return nullptr;
616 }
617 env->SetLongArrayRegion(jarr, 0, arr.size(),
618 reinterpret_cast<const jlong*>(arr.data()));
619 return jarr;
620}
621
622/**
623 * Convert an array of std::string into a jarray of jstring.
624 *
625 * @param env JRE environment.
626 * @param arr Array to convert.
627 */
628inline jobjectArray MakeJStringArray(JNIEnv* env,
629 std::span<const std::string> arr) {
630 static JClass stringCls{env, "java/lang/String"};
631 if (!stringCls) {
632 return nullptr;
633 }
634 jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls, nullptr);
635 if (!jarr) {
636 return nullptr;
637 }
638 for (size_t i = 0; i < arr.size(); ++i) {
639 JLocal<jstring> elem{env, MakeJString(env, arr[i])};
640 env->SetObjectArrayElement(jarr, i, elem.obj());
641 }
642 return jarr;
643}
644
645/**
646 * Convert an array of std::string into a jarray of jstring.
647 *
648 * @param env JRE environment.
649 * @param arr Array to convert.
650 */
651inline jobjectArray MakeJStringArray(JNIEnv* env,
652 std::span<std::string_view> arr) {
653 static JClass stringCls{env, "java/lang/String"};
654 if (!stringCls) {
655 return nullptr;
656 }
657 jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls, nullptr);
658 if (!jarr) {
659 return nullptr;
660 }
661 for (size_t i = 0; i < arr.size(); ++i) {
662 JLocal<jstring> elem{env, MakeJString(env, arr[i])};
663 env->SetObjectArrayElement(jarr, i, elem.obj());
664 }
665 return jarr;
666}
667
668/**
669 * Generic callback thread implementation.
670 *
671 * JNI's AttachCurrentThread() creates a Java Thread object on every
672 * invocation, which is both time inefficient and causes issues with Eclipse
673 * (which tries to keep a thread list up-to-date and thus gets swamped).
674 *
675 * Instead, this class attaches just once. When a hardware notification
676 * occurs, a condition variable wakes up this thread and this thread actually
677 * makes the call into Java.
678 *
679 * The template parameter T is the message being passed to the callback, but
680 * also needs to provide the following functions:
681 * static JavaVM* GetJVM();
682 * static const char* GetName();
683 * void CallJava(JNIEnv *env, jobject func, jmethodID mid);
684 */
685template <typename T>
687 public:
688 void Main() override;
689
690 std::queue<T> m_queue;
691 jobject m_func = nullptr;
692 jmethodID m_mid;
693};
694
695template <typename T>
696class JCallbackManager : public SafeThreadOwner<JCallbackThread<T>> {
697 public:
698 JCallbackManager() { this->SetJoinAtExit(false); }
699 void SetFunc(JNIEnv* env, jobject func, jmethodID mid);
700
701 template <typename... Args>
702 void Send(Args&&... args);
703};
704
705template <typename T>
706void JCallbackManager<T>::SetFunc(JNIEnv* env, jobject func, jmethodID mid) {
707 auto thr = this->GetThread();
708 if (!thr) {
709 return;
710 }
711 // free global reference
712 if (thr->m_func) {
713 env->DeleteGlobalRef(thr->m_func);
714 }
715 // create global reference
716 thr->m_func = env->NewGlobalRef(func);
717 thr->m_mid = mid;
718}
719
720template <typename T>
721template <typename... Args>
722void JCallbackManager<T>::Send(Args&&... args) {
723 auto thr = this->GetThread();
724 if (!thr) {
725 return;
726 }
727 thr->m_queue.emplace(std::forward<Args>(args)...);
728 thr->m_cond.notify_one();
729}
730
731template <typename T>
733 JNIEnv* env;
734 JavaVMAttachArgs args;
735 args.version = JNI_VERSION_1_2;
736 args.name = const_cast<char*>(T::GetName());
737 args.group = nullptr;
738 jint rs = T::GetJVM()->AttachCurrentThreadAsDaemon(
739 reinterpret_cast<void**>(&env), &args);
740 if (rs != JNI_OK) {
741 return;
742 }
743
744 std::unique_lock lock(m_mutex);
745 while (m_active) {
746 m_cond.wait(lock, [&] { return !(m_active && m_queue.empty()); });
747 if (!m_active) {
748 break;
749 }
750 while (!m_queue.empty()) {
751 if (!m_active) {
752 break;
753 }
754 auto item = std::move(m_queue.front());
755 m_queue.pop();
756 auto func = m_func;
757 auto mid = m_mid;
758 lock.unlock(); // don't hold mutex during callback execution
759 item.CallJava(env, func, mid);
760 if (env->ExceptionCheck()) {
761 env->ExceptionDescribe();
762 env->ExceptionClear();
763 }
764 lock.lock();
765 }
766 }
767
768 JavaVM* jvm = T::GetJVM();
769 if (jvm) {
770 jvm->DetachCurrentThread();
771 }
772}
773
774template <typename T>
776 public:
778 static JSingletonCallbackManager<T> instance;
779 return instance;
780 }
781};
782
783inline std::string GetJavaStackTrace(JNIEnv* env, std::string_view skipPrefix) {
784 // create a throwable
785 static JClass throwableCls(env, "java/lang/Throwable");
786 if (!throwableCls) {
787 return "";
788 }
789 static jmethodID constructorId = nullptr;
790 if (!constructorId) {
791 constructorId = env->GetMethodID(throwableCls, "<init>", "()V");
792 }
793 JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
794
795 // retrieve information from the exception.
796 // get method id
797 // getStackTrace returns an array of StackTraceElement
798 static jmethodID getStackTraceId = nullptr;
799 if (!getStackTraceId) {
800 getStackTraceId = env->GetMethodID(throwableCls, "getStackTrace",
801 "()[Ljava/lang/StackTraceElement;");
802 }
803
804 // call getStackTrace
805 JLocal<jobjectArray> stackTrace(
806 env, static_cast<jobjectArray>(
807 env->CallObjectMethod(throwable, getStackTraceId)));
808
809 if (!stackTrace) {
810 return "";
811 }
812
813 // get length of the array
814 jsize stackTraceLength = env->GetArrayLength(stackTrace);
815
816 // get toString methodId of StackTraceElement class
817 static JClass stackTraceElementCls(env, "java/lang/StackTraceElement");
818 if (!stackTraceElementCls) {
819 return "";
820 }
821 static jmethodID toStringId = nullptr;
822 if (!toStringId) {
823 toStringId = env->GetMethodID(stackTraceElementCls, "toString",
824 "()Ljava/lang/String;");
825 }
826
827 bool foundFirst = false;
828 std::string buf;
829 raw_string_ostream oss(buf);
830 for (jsize i = 0; i < stackTraceLength; i++) {
831 // add the result of toString method of each element in the result
832 JLocal<jobject> curStackTraceElement(
833 env, env->GetObjectArrayElement(stackTrace, i));
834
835 // call to string on the object
836 JLocal<jstring> stackElementString(
837 env, static_cast<jstring>(
838 env->CallObjectMethod(curStackTraceElement, toStringId)));
839
840 if (!stackElementString) {
841 return "";
842 }
843
844 // add a line to res
845 JStringRef elem(env, stackElementString);
846 if (!foundFirst) {
847 if (wpi::starts_with(elem, skipPrefix)) {
848 continue;
849 }
850 foundFirst = true;
851 }
852 oss << "\tat " << elem << '\n';
853 }
854
855 return oss.str();
856}
857
858inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func,
859 std::string_view excludeFuncPrefix) {
860 // create a throwable
861 static JClass throwableCls(env, "java/lang/Throwable");
862 if (!throwableCls) {
863 return "";
864 }
865 static jmethodID constructorId = nullptr;
866 if (!constructorId) {
867 constructorId = env->GetMethodID(throwableCls, "<init>", "()V");
868 }
869 JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
870
871 // retrieve information from the exception.
872 // get method id
873 // getStackTrace returns an array of StackTraceElement
874 static jmethodID getStackTraceId = nullptr;
875 if (!getStackTraceId) {
876 getStackTraceId = env->GetMethodID(throwableCls, "getStackTrace",
877 "()[Ljava/lang/StackTraceElement;");
878 }
879
880 // call getStackTrace
881 JLocal<jobjectArray> stackTrace(
882 env, static_cast<jobjectArray>(
883 env->CallObjectMethod(throwable, getStackTraceId)));
884
885 if (!stackTrace) {
886 return "";
887 }
888
889 // get length of the array
890 jsize stackTraceLength = env->GetArrayLength(stackTrace);
891
892 // get toString methodId of StackTraceElement class
893 static JClass stackTraceElementCls(env, "java/lang/StackTraceElement");
894 if (!stackTraceElementCls) {
895 return "";
896 }
897 static jmethodID toStringId = nullptr;
898 if (!toStringId) {
899 toStringId = env->GetMethodID(stackTraceElementCls, "toString",
900 "()Ljava/lang/String;");
901 }
902
903 bool haveLoc = false;
904 std::string buf;
905 raw_string_ostream oss(buf);
906 for (jsize i = 0; i < stackTraceLength; i++) {
907 // add the result of toString method of each element in the result
908 JLocal<jobject> curStackTraceElement(
909 env, env->GetObjectArrayElement(stackTrace, i));
910
911 // call to string on the object
912 JLocal<jstring> stackElementString(
913 env, static_cast<jstring>(
914 env->CallObjectMethod(curStackTraceElement, toStringId)));
915
916 if (!stackElementString) {
917 return "";
918 }
919
920 // add a line to res
921 JStringRef elem(env, stackElementString);
922 oss << elem << '\n';
923
924 if (func) {
925 // func is caller of immediate caller (if there was one)
926 // or, if we see it, the first user function
927 if (i == 1) {
928 *func = elem.str();
929 } else if (i > 1 && !haveLoc && !excludeFuncPrefix.empty() &&
930 !wpi::starts_with(elem, excludeFuncPrefix)) {
931 *func = elem.str();
932 haveLoc = true;
933 }
934 }
935 }
936
937 return oss.str();
938}
939
940/**
941 * Finds an exception class and keep it as a global reference.
942 *
943 * Similar to JClass, but provides Throw methods. Use with caution, as the
944 * destructor does NOT call DeleteGlobalRef due to potential shutdown issues
945 * with doing so.
946 */
947class JException : public JClass {
948 public:
949 JException() = default;
950 JException(JNIEnv* env, const char* name) : JClass(env, name) {
951 if (m_cls) {
952 m_constructor =
953 env->GetMethodID(m_cls, "<init>", "(Ljava/lang/String;)V");
954 }
955 }
956
957 void Throw(JNIEnv* env, jstring msg) {
958 jobject exception = env->NewObject(m_cls, m_constructor, msg);
959 env->Throw(static_cast<jthrowable>(exception));
960 }
961
962 void Throw(JNIEnv* env, std::string_view msg) {
963 Throw(env, MakeJString(env, msg));
964 }
965
966 explicit operator bool() const { return m_constructor; }
967
968 private:
969 jmethodID m_constructor = nullptr;
970};
971
973 const char* name;
975};
976
977} // namespace wpi::java
978
979#endif // WPIUTIL_WPI_JNI_UTIL_H_
This file defines the SmallString class.
This file defines the SmallVector class.
you may not use this file except in compliance with the License You may obtain a copy of the License at software distributed under the License is distributed on an AS IS WITHOUT WARRANTIES OR CONDITIONS OF ANY either express or implied See the License for the specific language governing permissions and limitations under the License LLVM Exceptions to the Apache License As an exception
Definition ThirdPartyNotices.txt:302
Definition SafeThread.h:33
Definition SafeThread.h:124
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:27
std::string_view str() const
Explicit conversion to std::string_view.
Definition SmallString.h:186
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition SmallVector.h:1212
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition sha1.h:30
void pop_back()
Definition SmallVector.h:441
void push_back(const T &Elt)
Definition SmallVector.h:429
size_t size() const
Definition SmallVector.h:99
pointer data()
Return a pointer to the vector's buffer, even if empty().
Definition SmallVector.h:302
iterator begin()
Definition SmallVector.h:283
void SetJoinAtExit(bool joinAtExit)
Definition SafeThread.h:106
Definition jni_util.h:696
JCallbackManager()
Definition jni_util.h:698
void SetFunc(JNIEnv *env, jobject func, jmethodID mid)
Definition jni_util.h:706
void Send(Args &&... args)
Definition jni_util.h:722
Generic callback thread implementation.
Definition jni_util.h:686
jmethodID m_mid
Definition jni_util.h:692
jobject m_func
Definition jni_util.h:691
std::queue< T > m_queue
Definition jni_util.h:690
void Main() override
Definition jni_util.h:732
Finds a class and keeps it as a global reference.
Definition jni_util.h:52
JClass(JNIEnv *env, const char *name)
Definition jni_util.h:56
jclass m_cls
Definition jni_util.h:77
void free(JNIEnv *env)
Definition jni_util.h:65
Finds an exception class and keep it as a global reference.
Definition jni_util.h:947
void Throw(JNIEnv *env, std::string_view msg)
Definition jni_util.h:962
JException(JNIEnv *env, const char *name)
Definition jni_util.h:950
void Throw(JNIEnv *env, jstring msg)
Definition jni_util.h:957
Definition jni_util.h:86
JGlobal(JNIEnv *env, T obj)
Definition jni_util.h:90
void free(JNIEnv *env)
Definition jni_util.h:94
T m_cls
Definition jni_util.h:106
Container class for cleaning up Java local references.
Definition jni_util.h:115
JLocal & operator=(const JLocal &)=delete
~JLocal()
Definition jni_util.h:129
JLocal(JLocal &&oth)
Definition jni_util.h:119
JLocal & operator=(JLocal &&oth)
Definition jni_util.h:123
T obj()
Definition jni_util.h:135
JLocal(JNIEnv *env, T obj)
Definition jni_util.h:117
JLocal(const JLocal &)=delete
Definition jni_util.h:775
static JSingletonCallbackManager< T > & GetInstance()
Definition jni_util.h:777
Java string (jstring) reference.
Definition jni_util.h:152
std::string_view str() const
Definition jni_util.h:172
WPI_String wpi_str() const
Definition jni_util.h:175
const char * c_str() const
Definition jni_util.h:173
size_t size() const
Definition jni_util.h:174
JStringRef(JNIEnv *env, jstring str)
Definition jni_util.h:154
Helper class for working with JNI arrays.
Definition jni_util.h:248
JSpanBase(const JSpanBase &)=delete
T & operator[](size_t i)
Definition jni_util.h:353
JSpanBase(JNIEnv *env, jarray_type jarr)
Definition jni_util.h:311
bool is_valid() const
Definition jni_util.h:343
T * end() const
Definition jni_util.h:341
JSpanBase & operator=(JSpanBase &&other)
Definition jni_util.h:266
JSpanBase(JNIEnv *env, jarray_type jarr, size_t size)
Definition jni_util.h:292
std::span< copy_cv_t< T, uint8_t >, Size > uarray() const
Definition jni_util.h:377
T * begin() const
Definition jni_util.h:339
const T & operator[](size_t i) const
Definition jni_util.h:351
size_t size() const
Definition jni_util.h:349
T * data() const
Definition jni_util.h:347
JSpanBase(JNIEnv *env, jobject bb, size_t size)
Definition jni_util.h:278
std::string_view str() const
Definition jni_util.h:367
JSpanBase(JSpanBase &&other)
Definition jni_util.h:256
std::span< T, Size > array() const
Definition jni_util.h:327
JSpanBase & operator=(const JSpanBase &)=delete
~JSpanBase()
Definition jni_util.h:314
A raw_ostream that writes to an std::string.
Definition raw_ostream.h:589
std::string & str()
Returns the string's reference.
Definition raw_ostream.h:607
Definition jni_util.h:210
#define WPI_JNI_MAKEJARRAY(T, F)
Definition jni_util.h:591
#define WPI_JNI_ARRAYHELPER(T, F)
Definition jni_util.h:186
detail namespace with internal helper functions
Definition input_adapters.h:32
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
constexpr bool is_qualification_convertible_v
Definition jni_util.h:228
typename copy_cv< CvSrc, Dest >::type copy_cv_t
Definition jni_util.h:225
Java Native Interface (JNI) utility functions.
Definition jni_util.h:29
jlongArray MakeJLongArray(JNIEnv *env, const T &arr)
Definition jni_util.h:612
jintArray MakeJIntArray(JNIEnv *env, std::span< const T > arr)
Convert a span to a jintArray.
Definition jni_util.h:484
jstring MakeJString(JNIEnv *env, std::string_view str)
Convert a UTF8 string into a jstring.
Definition jni_util.h:431
std::string GetJavaStackTrace(JNIEnv *env, std::string *func=nullptr, std::string_view excludeFuncPrefix={})
Gets a Java stack trace.
Definition jni_util.h:858
jbooleanArray MakeJBooleanArray(JNIEnv *env, std::span< const int > arr)
Convert an array of integers into a jbooleanArray.
Definition jni_util.h:549
jobjectArray MakeJStringArray(JNIEnv *env, std::span< const std::string > arr)
Convert an array of std::string into a jarray of jstring.
Definition jni_util.h:628
jbyteArray MakeJByteArray(JNIEnv *env, std::span< const uint8_t > str)
Convert a span into a jbyteArray.
Definition jni_util.h:533
bool convertUTF8ToUTF16String(std::string_view SrcUTF8, SmallVectorImpl< UTF16 > &DstUTF16)
Converts a UTF-8 string into a UTF-16 string with native endianness.
void print(wpi::raw_ostream &os, const S &format_str, Args &&... args)
Prints formatted data to the stream os.
Definition raw_ostream.h:25
constexpr WPI_String make_string(std::string_view view)
Converts a string_view to a WPI_String.
Definition string.h:33
bool Boolean
Definition ConvertUTF.h:130
bool convertUTF16ToUTF8String(std::span< const char > SrcBytes, SmallVectorImpl< char > &Out)
Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string.
constexpr bool starts_with(std::string_view str, std::string_view prefix) noexcept
Checks if str starts with the given prefix.
Definition StringExtras.h:235
A const UTF8 string.
Definition string.h:14
Definition jni_util.h:80
const char * name
Definition jni_util.h:81
JClass * cls
Definition jni_util.h:82
Definition jni_util.h:972
JException * cls
Definition jni_util.h:974
const char * name
Definition jni_util.h:973
Definition jni_util.h:184
Definition jni_util.h:441
static jintArray ToJava(JNIEnv *env, std::span< const T > arr)
Definition jni_util.h:442
Definition jni_util.h:214
U2 type
Definition jni_util.h:221