xref: /aosp_15_r20/external/fbjni/cxx/fbjni/detail/Exceptions.h (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 /**
18  * @file Exceptions.h
19  *
20  * After invoking a JNI function that can throw a Java exception, the macro
21  * @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref
22  * FACEBOOK_JNI_THROW_EXCEPTION_IF() should be invoked.
23  *
24  * IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!
25  * IMPORTANT! To use these methods you MUST call initExceptionHelpers() when
26  * your library is loaded.
27  */
28 
29 #pragma once
30 
31 #include <stdexcept>
32 #include <string>
33 
34 #include <jni.h>
35 
36 #include "Common.h"
37 #include "CoreClasses.h"
38 #include "References.h"
39 
40 #if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && \
41     !defined(FBJNI_NO_EXCEPTION_PTR)
42 // ARMv5 NDK does not support exception_ptr so we cannot use that when building
43 // for it.
44 #define FBJNI_NO_EXCEPTION_PTR
45 #endif
46 
47 namespace facebook {
48 namespace jni {
49 
50 class JThrowable;
51 
52 class JCppException : public JavaClass<JCppException, JThrowable> {
53  public:
54   static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;";
55 
create(const char * str)56   static local_ref<JCppException> create(const char* str) {
57     return newInstance(make_jstring(str));
58   }
59 
create(const std::exception & ex)60   static local_ref<JCppException> create(const std::exception& ex) {
61     return newInstance(make_jstring(ex.what()));
62   }
63 };
64 
65 // JniException
66 // ////////////////////////////////////////////////////////////////////////////////////
67 
68 /**
69  * This class wraps a Java exception into a C++ exception; if the exception is
70  * routed back to the Java side, it can be unwrapped and just look like a pure
71  * Java interaction. The class is resilient to errors while creating the
72  * exception, falling back to some pre-allocated exceptions if a new one cannot
73  * be allocated or populated.
74  *
75  * Note: the what() method of this class is not thread-safe (t6900503).
76  */
77 class JniException : public std::exception {
78  public:
79   JniException();
80   ~JniException() override;
81 
82   explicit JniException(alias_ref<jthrowable> throwable);
83 
84   JniException(JniException&& rhs);
85 
86   JniException(const JniException& other);
87 
88   local_ref<JThrowable> getThrowable() const noexcept;
89 
90   const char* what() const noexcept override;
91 
92   void setJavaException() const noexcept;
93 
94  private:
95   global_ref<JThrowable> throwable_;
96   mutable std::string what_;
97   mutable bool isMessageExtracted_;
98 
99   void populateWhat() const noexcept;
100 };
101 
102 // Exception throwing & translating functions
103 // //////////////////////////////////////////////////////
104 
105 // Functions that throw C++ exceptions
106 
107 static constexpr int kMaxExceptionMessageBufferSize = 512;
108 
109 // These methods are the preferred way to throw a Java exception from
110 // a C++ function.  They create and throw a C++ exception which wraps
111 // a Java exception, so the C++ flow is interrupted. Then, when
112 // translatePendingCppExceptionToJavaException is called at the
113 // topmost level of the native stack, the wrapped Java exception is
114 // thrown to the java caller.
115 template <typename... Args>
throwNewJavaException(const char * throwableName,const char * fmt,Args...args)116 [[noreturn]] void throwNewJavaException(
117     const char* throwableName,
118     const char* fmt,
119     Args... args) {
120   char msg[kMaxExceptionMessageBufferSize];
121   snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
122   throwNewJavaException(throwableName, msg);
123 }
124 
125 // Identifies any pending C++ exception and throws it as a Java exception. If
126 // the exception can't be thrown, it aborts the program.
127 void translatePendingCppExceptionToJavaException();
128 
129 #ifndef FBJNI_NO_EXCEPTION_PTR
130 local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
131 #endif
132 
133 /***
134  * The stack returned may include build ids.  It may be beneficial to
135  * call lyra::setLibraryIdentifierFunction before calling this if
136  * build ids are desirable.
137  */
138 local_ref<JThrowable> getJavaExceptionForCppBackTrace();
139 
140 local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
141 
142 // For convenience, some exception names in java.lang are available here.
143 const char* const gJavaLangIllegalArgumentException =
144     "java/lang/IllegalArgumentException";
145 
146 } // namespace jni
147 } // namespace facebook
148