1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "Exceptions.h"
4 
5 #include "JSIInteropModuleRegistry.h"
6 #include "JSReferencesCache.h"
7 
8 namespace jni = facebook::jni;
9 
10 namespace expo {
11 
12 jni::local_ref<CodedException> CodedException::create(const std::string &message) {
13   return CodedException::newInstance(jni::make_jstring(message));
14 }
15 
16 std::string CodedException::getCode() {
17   const auto getCode = this->getClass()->getMethod<jni::JString()>("getCode");
18   const auto code = getCode(this->self());
19   return code->toStdString();
20 }
21 
22 std::optional<std::string> CodedException::getLocalizedMessage() {
23   const auto getLocalizedMessage = this->getClass()
24     ->getMethod<jni::JString()>("getLocalizedMessage");
25   const auto message = getLocalizedMessage(this->self());
26   if (message != nullptr) {
27     return message->toStdString();
28   }
29 
30   return std::nullopt;
31 }
32 
33 jni::local_ref<JavaScriptEvaluateException> JavaScriptEvaluateException::create(
34   const std::string &message,
35   const std::string &jsStack
36 ) {
37   return JavaScriptEvaluateException::newInstance(
38     jni::make_jstring(message),
39     jni::make_jstring(jsStack)
40   );
41 }
42 
43 jni::local_ref<UnexpectedException> UnexpectedException::create(const std::string &message) {
44   return UnexpectedException::newInstance(
45     jni::make_jstring(message)
46   );
47 }
48 
49 jsi::Value makeCodedError(
50   jsi::Runtime &rt,
51   jsi::String code,
52   jsi::String message
53 ) {
54   auto codedErrorConstructor = rt
55     .global()
56     .getProperty(rt, "ExpoModulesCore_CodedError")
57     .asObject(rt)
58     .asFunction(rt);
59 
60   return codedErrorConstructor.callAsConstructor(
61     rt, {
62       jsi::Value(rt, code),
63       jsi::Value(rt, message)
64     }
65   );
66 }
67 
68 void rethrowAsCodedError(
69   jsi::Runtime &rt,
70   jni::JniException &jniException
71 ) {
72   jni::local_ref<jni::JThrowable> unboxedThrowable = jniException.getThrowable();
73   if (unboxedThrowable->isInstanceOf(CodedException::javaClassLocal())) {
74     auto codedException = jni::static_ref_cast<CodedException>(unboxedThrowable);
75     auto code = codedException->getCode();
76     auto message = codedException->getLocalizedMessage();
77 
78     auto codedError = makeCodedError(
79       rt,
80       jsi::String::createFromUtf8(rt, code),
81       jsi::String::createFromUtf8(rt, message.value_or(""))
82     );
83 
84     throw jsi::JSError(
85       message.value_or(""),
86       rt,
87       std::move(codedError)
88     );
89   }
90 
91   // Rethrow error if we can't wrap it.
92   throw;
93 }
94 
95 void throwPendingJniExceptionAsCppException() {
96   JNIEnv* env = jni::Environment::current();
97   if (env->ExceptionCheck() == JNI_FALSE) {
98     return;
99   }
100 
101   auto throwable = env->ExceptionOccurred();
102   if (!throwable) {
103     throw std::runtime_error("Unable to get pending JNI exception.");
104   }
105   env->ExceptionClear();
106 
107   throw jni::JniException(jni::adopt_local(throwable));
108 }
109 
110 void throwNewJavaException(jthrowable throwable) {
111   throw jni::JniException(jni::wrap_alias(throwable));
112 }
113 
114 } // namespace expo
115