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