|
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