ledger-core
djinni_support.hpp
1 //
2 // Copyright 2014 Dropbox, Inc.
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 #pragma once
18 
19 #include <cassert>
20 #include <exception>
21 #include <memory>
22 #include <mutex>
23 #include <string>
24 #include <vector>
25 
26 #include "../proxy_cache_interface.hpp"
27 #include "../djinni_common.hpp"
28 #include <jni.h>
29 
30 /*
31  * Djinni support library
32  */
33 
34 // jni.h should really put extern "C" in JNIEXPORT, but it doesn't. :(
35 #define CJNIEXPORT extern "C" JNIEXPORT
36 
37 namespace djinni {
38 
39 /*
40  * Global initialization and shutdown. Call these from JNI_OnLoad and JNI_OnUnload.
41  */
42 void jniInit(JavaVM * jvm);
43 void jniShutdown();
44 
45 /*
46  * Get the JNIEnv for the invoking thread. Should only be called on Java-created threads.
47  */
48 JNIEnv * jniGetThreadEnv();
49 
50 /*
51  * Global and local reference guard objects.
52  *
53  * A GlobalRef<T> is constructed with a local reference; the constructor upgrades the local
54  * reference to a global reference, and the destructor deletes the local ref.
55  *
56  * A LocalRef<T> should be constructed with a new local reference. The local reference will
57  * be deleted when the LocalRef is deleted.
58  */
59 struct GlobalRefDeleter { void operator() (jobject globalRef) noexcept; };
60 
61 template <typename PointerType>
62 class GlobalRef : public std::unique_ptr<typename std::remove_pointer<PointerType>::type,
63  GlobalRefDeleter> {
64 public:
65  GlobalRef() {}
66  GlobalRef(GlobalRef && obj)
67  : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::GlobalRefDeleter>(
68  std::move(obj)
69  ) {}
70  GlobalRef(JNIEnv * env, PointerType localRef)
71  : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::GlobalRefDeleter>(
72  static_cast<PointerType>(env->NewGlobalRef(localRef)),
74  ) {}
75 };
76 
77 struct LocalRefDeleter { void operator() (jobject localRef) noexcept; };
78 
79 template <typename PointerType>
80 class LocalRef : public std::unique_ptr<typename std::remove_pointer<PointerType>::type,
81  LocalRefDeleter> {
82 public:
83  LocalRef() {}
84  LocalRef(JNIEnv * /*env*/, PointerType localRef)
85  : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::LocalRefDeleter>(
86  localRef) {}
87  explicit LocalRef(PointerType localRef)
88  : std::unique_ptr<typename std::remove_pointer<PointerType>::type, LocalRefDeleter>(
89  localRef) {}
90  // Allow implicit conversion to PointerType so it can be passed
91  // as argument to JNI functions expecting PointerType.
92  // All functions creating new local references should return LocalRef instead of PointerType
93  operator PointerType() const & { return this->get(); }
94  operator PointerType() && = delete;
95 };
96 
97 template<class T>
98 const T& get(const T& x) noexcept { return x; }
99 template<class T>
100 typename LocalRef<T>::pointer get(const LocalRef<T>& x) noexcept { return x.get(); }
101 
102 template<class T>
103 const T& release(const T& x) noexcept { return x; }
104 template<class T>
105 typename LocalRef<T>::pointer release(LocalRef<T>& x) noexcept { return x.release(); }
106 template<class T>
107 typename LocalRef<T>::pointer release(LocalRef<T>&& x) noexcept { return x.release(); }
108 
109 /*
110  * Exception to indicate that a Java exception is pending in the JVM.
111  */
112 class jni_exception : public std::exception {
113  GlobalRef<jthrowable> m_java_exception;
114  std::string m_backtrace;
115 
116 public:
117  jni_exception(JNIEnv * env, jthrowable java_exception)
118  : m_java_exception(env, java_exception) {
119  assert(java_exception);
120  capture_backtrace();
121  }
122  jthrowable java_exception() const { return m_java_exception.get(); }
123 
124  const std::string get_backtrace() const { return m_backtrace; }
125 
126  /*
127  * Sets the pending JNI exception using this Java exception.
128  */
129  void set_as_pending(JNIEnv * env) const noexcept;
130 
131 private:
132  void capture_backtrace();
133 };
134 
135 /*
136  * Helpers method to work with Java strings
137  */
138 
139 /*
140  * Convert the given string in C++ std::string and releases the jstring instance.
141  */
142 void jniConvertAndReleaseJstring(JNIEnv * env, jstring& string, std::string & out);
143 
144 /*
145  * Throw if any Java exception is pending in the JVM.
146  *
147  * If an exception is pending, this function will clear the
148  * pending state, and pass the exception to
149  * jniThrowCppFromJavaException().
150  */
151 void jniExceptionCheck(JNIEnv * env);
152 
153 /*
154  * Throws a C++ exception based on the given Java exception.
155  *
156  * java_exception is a local reference to a Java throwable, which
157  * must not be null, and should no longer set as "pending" in the JVM.
158  * This is called to handle errors in other JNI processing, including
159  * by jniExceptionCheck().
160  *
161  * The default implementation is defined with __attribute__((weak)) so you
162  * can replace it by defining your own version. The default implementation
163  * will throw a jni_exception containing the given jthrowable.
164  */
165 DJINNI_NORETURN_DEFINITION
166 void jniThrowCppFromJavaException(JNIEnv * env, jthrowable java_exception);
167 
168 /*
169  * Set an AssertionError in env with message message, and then throw via jniExceptionCheck.
170  */
171 DJINNI_NORETURN_DEFINITION
172 void jniThrowAssertionError(JNIEnv * env, const char * file, int line, const char * check);
173 
174 #define DJINNI_ASSERT_MSG(check, env, message) \
175  do { \
176  ::djinni::jniExceptionCheck(env); \
177  const bool check__res = bool(check); \
178  ::djinni::jniExceptionCheck(env); \
179  if (!check__res) { \
180  ::djinni::jniThrowAssertionError(env, __FILE__, __LINE__, message); \
181  } \
182  } while(false)
183 #define DJINNI_ASSERT(check, env) DJINNI_ASSERT_MSG(check, env, #check)
184 
185 /*
186  * Helper for JniClass. (This can't be a subclass because it needs to not be templatized.)
187  */
189 
190  using registration_vec = std::vector<std::function<void()>>;
191  static registration_vec get_all();
192 
193 private:
194 
195  JniClassInitializer(std::function<void()> init);
196 
197  template <class C> friend class JniClass;
198  friend void jniInit(JavaVM *);
199 
200  static registration_vec & get_vec();
201  static std::mutex & get_mutex();
202 };
203 
204 /*
205  * Each instantiation of this template produces a singleton object of type C which
206  * will be initialized by djinni::jniInit(). For example:
207  *
208  * struct JavaFooInfo {
209  * jmethodID foo;
210  * JavaFooInfo() // initialize clazz and foo from jniGetThreadEnv
211  * }
212  *
213  * To use this in a JNI function or callback, invoke:
214  *
215  * CallVoidMethod(object, JniClass<JavaFooInfo>::get().foo, ...);
216  *
217  * This uses C++'s template instantiation behavior to guarantee that any T for which
218  * JniClass<T>::get() is *used* anywhere in the program will be *initialized* by init_all().
219  * Therefore, it's always safe to compile in wrappers for all known Java types - the library
220  * will only depend on the presence of those actually needed.
221  */
222 template <class C>
223 class JniClass {
224 public:
225  static const C & get() {
226  (void)s_initializer; // ensure that initializer is actually instantiated
227  assert(s_singleton);
228  return *s_singleton;
229  }
230 
231 private:
232  static const JniClassInitializer s_initializer;
233  static std::unique_ptr<C> s_singleton;
234 
235  static void allocate() {
236  // We can't use make_unique here, because C will have a private constructor and
237  // list JniClass as a friend; so we have to allocate it by hand.
238  s_singleton = std::unique_ptr<C>(new C());
239  }
240 };
241 
242 template <class C>
244 
245 template <class C>
246 std::unique_ptr<C> JniClass<C>::s_singleton;
247 
248 /*
249  * Exception-checking helpers. These will throw if an exception is pending.
250  */
251 GlobalRef<jclass> jniFindClass(const char * name);
252 jmethodID jniGetStaticMethodID(jclass clazz, const char * name, const char * sig);
253 jmethodID jniGetMethodID(jclass clazz, const char * name, const char * sig);
254 jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig);
255 
256 /*
257  * Helper for maintaining shared_ptrs to wrapped Java objects.
258  *
259  * This is used for automatically wrapping a Java object that exposes some interface
260  * with a C++ object that calls back into the JVM, such as a listener. Calling
261  * get_java_proxy<T>(obj) the first time will construct a T and return a shared_ptr to it, and
262  * also save a weak_ptr to the new object internally. The constructed T contains a strong
263  * GlobalRef to jobj. As long as something in C++ maintains a strong reference to the wrapper,
264  * future calls to get(jobj) will return the *same* wrapper object.
265  *
266  * Java | C++
267  * | ________________________ ___________
268  * _____________ | | | | |
269  * | | | | JniImplFooListener | <=========== | Foo |
270  * | FooListener | <============ | : public FooListener, | shared_ptr |___________|
271  * |_____________| GlobalRef | JavaProxyCacheEntry |
272  * | |________________________|
273  * | ^ ______________________
274  * | \ | |
275  * | - - - - - - | JavaProxyCache |
276  * | weak_ptr |______________________|
277  *
278  * As long as the C++ FooListener has references, the Java FooListener is kept alive.
279  *
280  * We use a custom unordered_map with Java objects (jobject) as keys, and JNI object
281  * identity and hashing functions. This means that as long as a key is in the map,
282  * we must have some other GlobalRef keeping it alive. To ensure safety, the Entry
283  * destructor removes *itself* from the map - destruction order guarantees that this
284  * will happen before the contained global reference becomes invalid (by destruction of
285  * the GlobalRef).
286  */
287 struct JavaIdentityHash;
288 struct JavaIdentityEquals;
290  using UnowningImplPointer = jobject;
291  using OwningImplPointer = jobject;
292  using OwningProxyPointer = std::shared_ptr<void>;
293  using WeakProxyPointer = std::weak_ptr<void>;
296 };
297 extern template class ProxyCache<JavaProxyCacheTraits>;
299 template <typename T> using JavaProxyHandle = JavaProxyCache::Handle<GlobalRef<jobject>, T>;
300 
301 /*
302  * Cache for CppProxy objects. This is the inverse of the JavaProxyCache mechanism above,
303  * ensuring that each time we pass an interface from Java to C++, we get the *same* CppProxy
304  * object on the Java side:
305  *
306  * Java | C++
307  * |
308  * ______________ | ________________ ___________
309  * | | | | | | |
310  * | Foo.CppProxy | ------------> | CppProxyHandle | =============> | Foo |
311  * |______________| (jlong) | <Foo> | (shared_ptr) |___________|
312  * ^ | |________________|
313  * \ |
314  * _________ | __________________
315  * | | | | |
316  * | WeakRef | <------------------------- | jniCppProxyCache |
317  * |_________| (GlobalRef) |__________________|
318  * |
319  *
320  * We don't use JNI WeakGlobalRef objects, because they last longer than is safe - a
321  * WeakGlobalRef can still be upgraded to a strong reference even during finalization, which
322  * leads to use-after-free. Java WeakRefs provide the right lifetime guarantee.
323  */
324 class JavaWeakRef;
326  using UnowningImplPointer = void *;
327  using OwningImplPointer = std::shared_ptr<void>;
328  using OwningProxyPointer = jobject;
330  using UnowningImplPointerHash = std::hash<void *>;
331  using UnowningImplPointerEqual = std::equal_to<void *>;
332 };
333 extern template class ProxyCache<JniCppProxyCacheTraits>;
336 
337 template <class T>
338 static const std::shared_ptr<T> & objectFromHandleAddress(jlong handle) {
339  assert(handle);
340  assert(handle > 4096);
341  // Below line segfaults gcc-4.8. Using a temporary variable hides the bug.
342  //const auto & ret = reinterpret_cast<const CppProxyHandle<T> *>(handle)->get();
343  const CppProxyHandle<T> *proxy_handle =
344  reinterpret_cast<const CppProxyHandle<T> *>(handle);
345  const auto & ret = proxy_handle->get();
346  assert(ret);
347  return ret;
348 }
349 
350 /*
351  * Information needed to use a CppProxy class.
352  *
353  * In an ideal world, this object would be properly always-valid RAII, and we'd use an
354  * optional<CppProxyClassInfo> where needed. Unfortunately we don't want to depend on optional
355  * here, so this object has an invalid state and default constructor.
356  */
358  const GlobalRef<jclass> clazz;
359  const jmethodID constructor;
360  const jfieldID idField;
361 
362  CppProxyClassInfo(const char * className);
365 
366  // Validity check
367  explicit operator bool() const { return bool(clazz); }
368 };
369 
370 /*
371  * Base class for Java <-> C++ interface adapters.
372  *
373  * I is the C++ base class (interface) being adapted; Self is the interface adapter class
374  * derived from JniInterface (using CRTP). For example:
375  *
376  * class NativeToken final : djinni::JniInterface<Token, NativeToken> { ... }
377  */
378 template <class I, class Self>
380 public:
381  /*
382  * Given a C++ object, find or create a Java version. The cases here are:
383  * 1. Null
384  * 2. The provided C++ object is actually a JavaProxy (C++-side proxy for Java impl)
385  * 3. The provided C++ object has an existing CppProxy (Java-side proxy for C++ impl)
386  * 4. The provided C++ object needs a new CppProxy allocated
387  */
388  jobject _toJava(JNIEnv* jniEnv, const std::shared_ptr<I> & c) const {
389  // Case 1 - null
390  if (!c) {
391  return nullptr;
392  }
393 
394  // Case 2 - already a JavaProxy. Only possible if Self::JavaProxy exists.
395  if (jobject impl = _unwrapJavaProxy<Self>(&c)) {
396  return jniEnv->NewLocalRef(impl);
397  }
398 
399  // Cases 3 and 4.
400  assert(m_cppProxyClass);
401  return JniCppProxyCache::get(typeid(c), c, &newCppProxy);
402 
403  }
404 
405  /*
406  * Given a Java object, find or create a C++ version. The cases here are:
407  * 1. Null
408  * 2. The provided Java object is actually a CppProxy (Java-side proxy for a C++ impl)
409  * 3. The provided Java object has an existing JavaProxy (C++-side proxy for a Java impl)
410  * 4. The provided Java object needs a new JavaProxy allocated
411  */
412  std::shared_ptr<I> _fromJava(JNIEnv* jniEnv, jobject j) const {
413  // Case 1 - null
414  if (!j) {
415  return nullptr;
416  }
417 
418  // Case 2 - already a Java proxy; we just need to pull the C++ impl out. (This case
419  // is only possible if we were constructed with a cppProxyClassName parameter.)
420  if (m_cppProxyClass
421  && jniEnv->IsSameObject(jniEnv->GetObjectClass(j), m_cppProxyClass.clazz.get())) {
422  jlong handle = jniEnv->GetLongField(j, m_cppProxyClass.idField);
423  jniExceptionCheck(jniEnv);
424  return objectFromHandleAddress<I>(handle);
425  }
426 
427  // Cases 3 and 4 - see _getJavaProxy helper below. JavaProxyCache is responsible for
428  // distinguishing between the two cases. Only possible if Self::JavaProxy exists.
429  return _getJavaProxy<Self>(j);
430  }
431 
432  // Constructor for interfaces for which a Java-side CppProxy class exists
433  JniInterface(const char * cppProxyClassName) : m_cppProxyClass(cppProxyClassName) {}
434 
435  // Constructor for interfaces without a Java proxy class
436  JniInterface() : m_cppProxyClass{} {}
437 
438 private:
439  /*
440  * Helpers for _toJava above. The possibility that an object is already a C++-side proxy
441  * only exists if the code generator emitted one (if Self::JavaProxy exists).
442  */
443  template <typename S, typename JavaProxy = typename S::JavaProxy>
444  jobject _unwrapJavaProxy(const std::shared_ptr<I> * c) const {
445  if (auto proxy = dynamic_cast<JavaProxy *>(c->get())) {
446  return proxy->JavaProxyHandle<JavaProxy>::get().get();
447  } else {
448  return nullptr;
449  }
450  }
451 
452  template <typename S>
453  jobject _unwrapJavaProxy(...) const {
454  return nullptr;
455  }
456 
457  /*
458  * Helper for _toJava above: given a C++ object, allocate a CppProxy on the Java side for
459  * it. This is actually called by jniCppProxyCacheGet, which holds a lock on the global
460  * C++-to-Java proxy map object.
461  */
462  static std::pair<jobject, void*> newCppProxy(const std::shared_ptr<void> & cppObj) {
463  const auto & data = JniClass<Self>::get();
464  const auto & jniEnv = jniGetThreadEnv();
465  std::unique_ptr<CppProxyHandle<I>> to_encapsulate(
466  new CppProxyHandle<I>(std::static_pointer_cast<I>(cppObj)));
467  jlong handle = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get()));
468  jobject cppProxy = jniEnv->NewObject(data.m_cppProxyClass.clazz.get(),
469  data.m_cppProxyClass.constructor,
470  handle);
471  jniExceptionCheck(jniEnv);
472  to_encapsulate.release();
473  return { cppProxy, cppObj.get() };
474  }
475 
476  /*
477  * Helpers for _fromJava above. We can only produce a C++-side proxy if the code generator
478  * emitted one (if Self::JavaProxy exists).
479  */
480  template <typename S, typename JavaProxy = typename S::JavaProxy>
481  std::shared_ptr<I> _getJavaProxy(jobject j) const {
482  static_assert(std::is_base_of<JavaProxyHandle<JavaProxy>, JavaProxy>::value,
483  "JavaProxy must derive from JavaProxyCacheEntry");
484 
485  return std::static_pointer_cast<JavaProxy>(JavaProxyCache::get(
486  typeid(JavaProxy), j,
487  [] (const jobject & obj) -> std::pair<std::shared_ptr<void>, jobject> {
488  auto ret = std::make_shared<JavaProxy>(obj);
489  return { ret, ret->JavaProxyHandle<JavaProxy>::get().get() };
490  }
491  ));
492  }
493 
494  template <typename S>
495  std::shared_ptr<I> _getJavaProxy(...) const {
496  assert(false);
497  return nullptr;
498  }
499 
500  const CppProxyClassInfo m_cppProxyClass;
501 };
502 
503 /*
504  * Guard object which automatically begins and ends a JNI local frame when
505  * it is created and destroyed, using PushLocalFrame and PopLocalFrame.
506  *
507  * Local frame creation can fail. The throwOnError parameter specifies how
508  * errors are reported:
509  * - true (default): throws on failure
510  * - false: queues a JNI exception on failure; the user must call checkSuccess()
511  *
512  * The JNIEnv given at construction is expected to still be valid at
513  * destruction, so this class isn't suitable for use across threads.
514  * It is intended for use on the stack.
515  *
516  * All JNI local references created within the defined scope will be
517  * released at the end of the scope. This class doesn't support
518  * the jobject return value supported by PopLocalFrame(), because
519  * the destructor cannot return the new reference value for the parent
520  * frame.
521  */
523 public:
524  /*
525  * Create the guard object and begin the local frame.
526  *
527  * @param p_env the JNIEnv for the current thread.
528  * @param capacity the initial number of local references to
529  * allocate.
530  */
531  JniLocalScope(JNIEnv* p_env, jint capacity, bool throwOnError = true);
532  bool checkSuccess() const { return m_success; }
533  ~JniLocalScope();
534 private:
535  JniLocalScope(const JniLocalScope& other);
536  JniLocalScope& operator=(const JniLocalScope& other);
537 
538  static bool _pushLocalFrame(JNIEnv* const env, jint capacity);
539  static void _popLocalFrame(JNIEnv* const env, jobject returnRef);
540 
541  JNIEnv* const m_env;
542  const bool m_success;
543 };
544 
545 jstring jniStringFromUTF8(JNIEnv * env, const std::string & str);
546 std::string jniUTF8FromString(JNIEnv * env, const jstring jstr);
547 
548 jstring jniStringFromWString(JNIEnv * env, const std::wstring & str);
549 std::wstring jniWStringFromString(JNIEnv * env, const jstring jstr);
550 
551 class JniEnum {
552 public:
553  /*
554  * Given a Java object, find its numeric value. This returns a jint, which the caller can
555  * static_cast<> into the necessary C++ enum type.
556  */
557  jint ordinal(JNIEnv * env, jobject obj) const;
558 
559  /*
560  * Create a Java value of the wrapped class with the given value.
561  */
562  LocalRef<jobject> create(JNIEnv * env, jint value) const;
563 
564 protected:
565  JniEnum(const std::string & name);
566  jclass enumClass() const { return m_clazz.get(); }
567 
568 private:
569  const GlobalRef<jclass> m_clazz;
570  const jmethodID m_staticmethValues;
571  const jmethodID m_methOrdinal;
572 };
573 
574 class JniFlags : private JniEnum {
575 public:
576  /*
577  * Given a Java EnumSet convert it to the corresponding bit pattern
578  * which can then be static_cast<> to the actual enum.
579  */
580  unsigned flags(JNIEnv * env, jobject obj) const;
581 
582  /*
583  * Create a Java EnumSet of the specified flags considering the given number of active bits.
584  */
585  LocalRef<jobject> create(JNIEnv * env, unsigned flags, int bits) const;
586 
587  using JniEnum::create;
588 
589 protected:
590  JniFlags(const std::string & name);
591 
592 private:
593  const GlobalRef<jclass> m_clazz { jniFindClass("java/util/EnumSet") };
594  const jmethodID m_methNoneOf { jniGetStaticMethodID(m_clazz.get(), "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;") };
595  const jmethodID m_methAdd { jniGetMethodID(m_clazz.get(), "add", "(Ljava/lang/Object;)Z") };
596  const jmethodID m_methIterator { jniGetMethodID(m_clazz.get(), "iterator", "()Ljava/util/Iterator;") };
597  const jmethodID m_methSize { jniGetMethodID(m_clazz.get(), "size", "()I") };
598 
599  struct {
600  const GlobalRef<jclass> clazz { jniFindClass("java/util/Iterator") };
601  const jmethodID methNext { jniGetMethodID(clazz.get(), "next", "()Ljava/lang/Object;") };
602  } m_iterator;
603 };
604 
605 #define DJINNI_FUNCTION_PROLOGUE0(env_)
606 #define DJINNI_FUNCTION_PROLOGUE1(env_, arg1_)
607 
608 /*
609  * Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN.
610  *
611  * Must be called in a catch block. Responsible for setting the pending
612  * exception in JNI based on the current C++ exception.
613  *
614  * The default implementation is defined with __attribute__((weak)) so you
615  * can replace it by defining your own version. The default implementation
616  * will call jniDefaultSetPendingFromCurrent(), which will propagate a
617  * jni_exception directly into Java, or throw a RuntimeException for any
618  * other std::exception.
619  */
620 void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept;
621 
622 /*
623  * Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN.
624  *
625  * Must be called in a catch block. Responsible for setting the pending
626  * exception in JNI based on the current C++ exception.
627  *
628  * This will call jniSetPendingFrom(env, jni_exception) if the current exception
629  * is a jni_exception, or otherwise will set a RuntimeException from any
630  * other std::exception. Any non-std::exception will result in a call
631  * to terminate().
632  *
633  * This is called by the default implementation of jniSetPendingFromCurrent.
634  */
635 void jniDefaultSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept;
636 
637 /* Catch C++ exceptions and translate them to Java exceptions.
638  *
639  * All functions called by Java must be fully wrapped by an outer try...catch block like so:
640  *
641  * try {
642  * ...
643  * } JNI_TRANSLATE_EXCEPTIONS_RETURN(env, 0)
644  * ... or JNI_TRANSLATE_EXCEPTIONS_RETURN(env, ) for functions returning void
645  *
646  * The second parameter is a default return value to be used if an exception is caught and
647  * converted. (For JNI outer-layer calls, this result will always be ignored by JNI, so
648  * it can safely be 0 for any function with a non-void return value.)
649  */
650 #define JNI_TRANSLATE_EXCEPTIONS_RETURN(env, ret) \
651  catch (const std::exception &) { \
652  ::djinni::jniSetPendingFromCurrent(env, __func__); \
653  return ret; \
654  }
655 
656 /* Catch jni_exception and translate it back to a Java exception, without catching
657  * any other C++ exceptions. Can be used to wrap code which might cause JNI
658  * exceptions like so:
659  *
660  * try {
661  * ...
662  * } JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, 0)
663  * ... or JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, ) for functions returning void
664  *
665  * The second parameter is a default return value to be used if an exception is caught and
666  * converted. (For JNI outer-layer calls, this result will always be ignored by JNI, so
667  * it can safely be 0 for any function with a non-void return value.)
668  */
669 #define JNI_TRANSLATE_JNI_EXCEPTIONS_RETURN(env, ret) \
670  catch (const ::djinni::jni_exception & e) { \
671  e.set_as_pending(env); \
672  return ret; \
673  }
674 
675 } // namespace djinni
Definition: djinni_support.hpp:59
Definition: djinni_support.hpp:357
Definition: djinni_support.hpp:325
Definition: djinni_support.cpp:217
Definition: djinni_support.hpp:551
Definition: djinni_support.hpp:80
Definition: djinni_support.hpp:188
Definition: djinni_support.hpp:112
Definition: djinni_support.cpp:27
Definition: proxy_cache_interface.hpp:101
Definition: djinni_support.cpp:216
Definition: proxy_cache_interface.hpp:131
Definition: djinni_support.hpp:77
Definition: djinni_support.hpp:522
Definition: djinni_support.hpp:223
Definition: djinni_support.hpp:379
Definition: djinni_support.hpp:574
Definition: djinni_support.cpp:679
Definition: djinni_support.hpp:289
Definition: djinni_support.hpp:62