|
1 | 1 | #include "firestore/src/android/exception_android.h"
|
2 | 2 |
|
3 |
| -#include <cstring> |
4 |
| - |
5 |
| -#include "firestore/src/android/util_android.h" |
| 3 | +#include "app/src/util_android.h" |
| 4 | +#include "firestore/src/jni/env.h" |
| 5 | +#include "firestore/src/jni/loader.h" |
| 6 | +#include "firestore/src/jni/throwable.h" |
6 | 7 |
|
7 | 8 | namespace firebase {
|
8 | 9 | namespace firestore {
|
| 10 | +namespace { |
| 11 | + |
| 12 | +using jni::Constructor; |
| 13 | +using jni::Env; |
| 14 | +using jni::Local; |
| 15 | +using jni::Method; |
| 16 | +using jni::Object; |
| 17 | +using jni::StaticMethod; |
| 18 | +using jni::String; |
| 19 | +using jni::Throwable; |
| 20 | + |
| 21 | +// FirebaseFirestoreException |
| 22 | +constexpr char kFirestoreExceptionClassName[] = PROGUARD_KEEP_CLASS |
| 23 | + "com/google/firebase/firestore/FirebaseFirestoreException"; |
| 24 | + |
| 25 | +Constructor<Throwable> kNewFirestoreException( |
| 26 | + "(Ljava/lang/String;" |
| 27 | + "Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;)V"); |
| 28 | +Method<Object> kGetCode( |
| 29 | + "getCode", |
| 30 | + "()Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;"); |
| 31 | + |
| 32 | +jclass g_firestore_exception_class = nullptr; |
| 33 | + |
| 34 | +// FirebaseFirestoreException$Code |
| 35 | +constexpr char kCodeClassName[] = PROGUARD_KEEP_CLASS |
| 36 | + "com/google/firebase/firestore/FirebaseFirestoreException$Code"; |
| 37 | + |
| 38 | +Method<int32_t> kValue("value", "()I"); |
| 39 | +StaticMethod<Object> kFromValue( |
| 40 | + "fromValue", |
| 41 | + "(I)Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;"); |
9 | 42 |
|
10 |
| -// clang-format off |
11 |
| -#define FIRESTORE_EXCEPTION_METHODS(X) \ |
12 |
| - X(Constructor, "<init>", \ |
13 |
| - "(Ljava/lang/String;" \ |
14 |
| - "Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;)V"), \ |
15 |
| - X(GetCode, "getCode", \ |
16 |
| - "()Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;") |
17 |
| -// clang-format on |
18 |
| - |
19 |
| -METHOD_LOOKUP_DECLARATION(firestore_exception, FIRESTORE_EXCEPTION_METHODS) |
20 |
| -METHOD_LOOKUP_DEFINITION( |
21 |
| - firestore_exception, |
22 |
| - PROGUARD_KEEP_CLASS |
23 |
| - "com/google/firebase/firestore/FirebaseFirestoreException", |
24 |
| - FIRESTORE_EXCEPTION_METHODS) |
25 |
| - |
26 |
| -// clang-format off |
27 |
| -#define FIRESTORE_EXCEPTION_CODE_METHODS(X) \ |
28 |
| - X(Value, "value", "()I"), \ |
29 |
| - X(FromValue, "fromValue", \ |
30 |
| - "(I)Lcom/google/firebase/firestore/FirebaseFirestoreException$Code;", \ |
31 |
| - util::kMethodTypeStatic) |
32 |
| -// clang-format on |
33 |
| - |
34 |
| -METHOD_LOOKUP_DECLARATION(firestore_exception_code, |
35 |
| - FIRESTORE_EXCEPTION_CODE_METHODS) |
36 |
| -METHOD_LOOKUP_DEFINITION( |
37 |
| - firestore_exception_code, |
38 |
| - PROGUARD_KEEP_CLASS |
39 |
| - "com/google/firebase/firestore/FirebaseFirestoreException$Code", |
40 |
| - FIRESTORE_EXCEPTION_CODE_METHODS) |
41 |
| - |
42 |
| -#define ILLEGAL_STATE_EXCEPTION_METHODS(X) X(Constructor, "<init>", "()V") |
43 |
| - |
44 |
| -METHOD_LOOKUP_DECLARATION(illegal_state_exception, |
45 |
| - ILLEGAL_STATE_EXCEPTION_METHODS) |
46 |
| -METHOD_LOOKUP_DEFINITION(illegal_state_exception, |
47 |
| - PROGUARD_KEEP_CLASS "java/lang/IllegalStateException", |
48 |
| - ILLEGAL_STATE_EXCEPTION_METHODS) |
| 43 | +// IllegalStateException |
| 44 | +constexpr char kIllegalStateExceptionClassName[] = |
| 45 | + PROGUARD_KEEP_CLASS "java/lang/IllegalStateException"; |
| 46 | +Constructor<Throwable> kNewIllegalStateException("()V"); |
| 47 | + |
| 48 | +jclass g_illegal_state_exception_class = nullptr; |
| 49 | + |
| 50 | +} // namespace |
49 | 51 |
|
50 | 52 | /* static */
|
51 |
| -Error ExceptionInternal::GetErrorCode(JNIEnv* env, jobject exception) { |
52 |
| - if (exception == nullptr) { |
| 53 | +void ExceptionInternal::Initialize(jni::Loader& loader) { |
| 54 | + g_firestore_exception_class = loader.LoadClass( |
| 55 | + kFirestoreExceptionClassName, kNewFirestoreException, kGetCode); |
| 56 | + |
| 57 | + loader.LoadClass(kCodeClassName, kValue, kFromValue); |
| 58 | + |
| 59 | + g_illegal_state_exception_class = loader.LoadClass( |
| 60 | + kIllegalStateExceptionClassName, kNewIllegalStateException); |
| 61 | +} |
| 62 | + |
| 63 | +Error ExceptionInternal::GetErrorCode(Env& env, const Object& exception) { |
| 64 | + if (!exception) { |
53 | 65 | return Error::kErrorOk;
|
54 | 66 | }
|
55 | 67 |
|
56 |
| - // Some of the Precondition failure is thrown as IllegalStateException instead |
57 |
| - // of a FirebaseFirestoreException. So we convert them into a more meaningful |
58 |
| - // code. |
59 |
| - if (env->IsInstanceOf(exception, illegal_state_exception::GetClass())) { |
| 68 | + if (IsIllegalStateException(env, exception)) { |
| 69 | + // Some of the Precondition failure is thrown as IllegalStateException |
| 70 | + // instead of a FirebaseFirestoreException. Convert those into a more |
| 71 | + // meaningful code. |
60 | 72 | return Error::kErrorFailedPrecondition;
|
61 | 73 | } else if (!IsFirestoreException(env, exception)) {
|
62 | 74 | return Error::kErrorUnknown;
|
63 | 75 | }
|
64 | 76 |
|
65 |
| - jobject code = env->CallObjectMethod( |
66 |
| - exception, |
67 |
| - firestore_exception::GetMethodId(firestore_exception::kGetCode)); |
68 |
| - jint code_value = env->CallIntMethod( |
69 |
| - code, |
70 |
| - firestore_exception_code::GetMethodId(firestore_exception_code::kValue)); |
71 |
| - env->DeleteLocalRef(code); |
72 |
| - CheckAndClearJniExceptions(env); |
73 |
| - |
74 |
| - if (code_value > Error::kErrorUnauthenticated || |
75 |
| - code_value < Error::kErrorOk) { |
| 77 | + Local<Object> java_code = env.Call(exception, kGetCode); |
| 78 | + int32_t code = env.Call(java_code, kValue); |
| 79 | + |
| 80 | + if (code > Error::kErrorUnauthenticated || code < Error::kErrorOk) { |
76 | 81 | return Error::kErrorUnknown;
|
77 | 82 | }
|
78 |
| - return static_cast<Error>(code_value); |
| 83 | + return static_cast<Error>(code); |
79 | 84 | }
|
80 | 85 |
|
81 |
| -/* static */ |
82 |
| -std::string ExceptionInternal::ToString(JNIEnv* env, jobject exception) { |
83 |
| - return util::GetMessageFromException(env, exception); |
| 86 | +std::string ExceptionInternal::ToString(Env& env, const Object& exception) { |
| 87 | + return util::GetMessageFromException(env.get(), exception.get()); |
84 | 88 | }
|
85 | 89 |
|
86 |
| -/* static */ |
87 |
| -jthrowable ExceptionInternal::Create(JNIEnv* env, Error code, |
88 |
| - const char* message) { |
| 90 | +Local<Throwable> ExceptionInternal::Create(Env& env, Error code, |
| 91 | + const std::string& message) { |
89 | 92 | if (code == Error::kErrorOk) {
|
90 |
| - return nullptr; |
| 93 | + return {}; |
91 | 94 | }
|
92 |
| - // FirebaseFirestoreException requires message to be non-empty. If the caller |
93 |
| - // does not bother to give details, we assign an arbitrary message here. |
94 |
| - if (message == nullptr || strlen(message) == 0) { |
95 |
| - message = "Unknown Exception"; |
| 95 | + |
| 96 | + Local<String> java_message; |
| 97 | + if (message.empty()) { |
| 98 | + // FirebaseFirestoreException requires message to be non-empty. If the |
| 99 | + // caller does not bother to give details, we assign an arbitrary message |
| 100 | + // here. |
| 101 | + java_message = env.NewStringUtf("Unknown Exception"); |
| 102 | + } else { |
| 103 | + java_message = env.NewStringUtf(message); |
96 | 104 | }
|
97 | 105 |
|
98 |
| - jstring exception_message = env->NewStringUTF(message); |
99 |
| - jobject exception_code = |
100 |
| - env->CallStaticObjectMethod(firestore_exception_code::GetClass(), |
101 |
| - firestore_exception_code::GetMethodId( |
102 |
| - firestore_exception_code::kFromValue), |
103 |
| - static_cast<jint>(code)); |
104 |
| - jthrowable result = static_cast<jthrowable>(env->NewObject( |
105 |
| - firestore_exception::GetClass(), |
106 |
| - firestore_exception::GetMethodId(firestore_exception::kConstructor), |
107 |
| - exception_message, exception_code)); |
108 |
| - env->DeleteLocalRef(exception_message); |
109 |
| - env->DeleteLocalRef(exception_code); |
110 |
| - CheckAndClearJniExceptions(env); |
111 |
| - return result; |
| 106 | + Local<Object> java_code = env.Call(kFromValue, static_cast<int32_t>(code)); |
| 107 | + return env.New(kNewFirestoreException, java_message, java_code); |
112 | 108 | }
|
113 | 109 |
|
114 |
| -/* static */ |
115 |
| -jthrowable ExceptionInternal::Wrap(JNIEnv* env, jthrowable exception) { |
| 110 | +Local<Throwable> ExceptionInternal::Wrap(Env& env, |
| 111 | + Local<Throwable>&& exception) { |
116 | 112 | if (IsFirestoreException(env, exception)) {
|
117 |
| - return static_cast<jthrowable>(env->NewLocalRef(exception)); |
| 113 | + return Move(exception); |
118 | 114 | } else {
|
119 | 115 | return Create(env, GetErrorCode(env, exception),
|
120 | 116 | ToString(env, exception).c_str());
|
121 | 117 | }
|
122 | 118 | }
|
123 | 119 |
|
124 |
| -/* static */ |
125 |
| -bool ExceptionInternal::IsFirestoreException(JNIEnv* env, jobject exception) { |
126 |
| - return env->IsInstanceOf(exception, firestore_exception::GetClass()); |
| 120 | +bool ExceptionInternal::IsFirestoreException(Env& env, |
| 121 | + const Object& exception) { |
| 122 | + return env.IsInstanceOf(exception, g_firestore_exception_class); |
127 | 123 | }
|
128 | 124 |
|
129 |
| -/* static */ |
130 |
| -bool ExceptionInternal::IsAnyExceptionThrownByFirestore(JNIEnv* env, |
131 |
| - jobject exception) { |
132 |
| - return IsFirestoreException(env, exception) || |
133 |
| - env->IsInstanceOf(exception, illegal_state_exception::GetClass()); |
134 |
| -} |
135 |
| - |
136 |
| -/* static */ |
137 |
| -bool ExceptionInternal::Initialize(App* app) { |
138 |
| - JNIEnv* env = app->GetJNIEnv(); |
139 |
| - jobject activity = app->activity(); |
140 |
| - bool result = firestore_exception::CacheMethodIds(env, activity) && |
141 |
| - firestore_exception_code::CacheMethodIds(env, activity) && |
142 |
| - illegal_state_exception::CacheMethodIds(env, activity); |
143 |
| - util::CheckAndClearJniExceptions(env); |
144 |
| - return result; |
| 125 | +bool ExceptionInternal::IsIllegalStateException(Env& env, |
| 126 | + const Object& exception) { |
| 127 | + return env.IsInstanceOf(exception, g_illegal_state_exception_class); |
145 | 128 | }
|
146 | 129 |
|
147 |
| -/* static */ |
148 |
| -void ExceptionInternal::Terminate(App* app) { |
149 |
| - JNIEnv* env = app->GetJNIEnv(); |
150 |
| - firestore_exception::ReleaseClass(env); |
151 |
| - firestore_exception_code::ReleaseClass(env); |
152 |
| - illegal_state_exception::ReleaseClass(env); |
153 |
| - util::CheckAndClearJniExceptions(env); |
| 130 | +bool ExceptionInternal::IsAnyExceptionThrownByFirestore( |
| 131 | + Env& env, const Object& exception) { |
| 132 | + return IsFirestoreException(env, exception) || |
| 133 | + IsIllegalStateException(env, exception); |
154 | 134 | }
|
155 | 135 |
|
156 | 136 | } // namespace firestore
|
|
0 commit comments