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 
create(const std::string & message)12 jni::local_ref<CodedException> CodedException::create(const std::string &message) {
13   return CodedException::newInstance(jni::make_jstring(message));
14 }
15 
getCode()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 
getLocalizedMessage()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 
create(const std::string & message,const std::string & jsStack)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 
create(const std::string & message)43 jni::local_ref<UnexpectedException> UnexpectedException::create(const std::string &message) {
44   return UnexpectedException::newInstance(
45     jni::make_jstring(message)
46   );
47 }
48 
create(int received,int expected)49 jni::local_ref<InvalidArgsNumberException> InvalidArgsNumberException::create(int received, int expected) {
50   return InvalidArgsNumberException::newInstance(
51     received,
52     expected,
53     expected // number of required arguments
54   );
55 }
56 
makeCodedError(jsi::Runtime & rt,jsi::String code,jsi::String message)57 jsi::Value makeCodedError(
58   jsi::Runtime &rt,
59   jsi::String code,
60   jsi::String message
61 ) {
62   auto codedErrorConstructor = rt
63     .global()
64     .getProperty(rt, "ExpoModulesCore_CodedError")
65     .asObject(rt)
66     .asFunction(rt);
67 
68   return codedErrorConstructor.callAsConstructor(
69     rt, {
70       jsi::Value(rt, code),
71       jsi::Value(rt, message)
72     }
73   );
74 }
75 
rethrowAsCodedError(jsi::Runtime & rt,jni::JniException & jniException)76 void rethrowAsCodedError(
77   jsi::Runtime &rt,
78   jni::JniException &jniException
79 ) {
80   jni::local_ref<jni::JThrowable> unboxedThrowable = jniException.getThrowable();
81   if (unboxedThrowable->isInstanceOf(CodedException::javaClassLocal())) {
82     auto codedException = jni::static_ref_cast<CodedException>(unboxedThrowable);
83     auto code = codedException->getCode();
84     auto message = codedException->getLocalizedMessage();
85 
86     auto codedError = makeCodedError(
87       rt,
88       jsi::String::createFromUtf8(rt, code),
89       jsi::String::createFromUtf8(rt, message.value_or(""))
90     );
91 
92     throw jsi::JSError(
93       message.value_or(""),
94       rt,
95       std::move(codedError)
96     );
97   }
98 
99   // Rethrow error if we can't wrap it.
100   throw;
101 }
102 
throwPendingJniExceptionAsCppException()103 void throwPendingJniExceptionAsCppException() {
104   JNIEnv* env = jni::Environment::current();
105   if (env->ExceptionCheck() == JNI_FALSE) {
106     return;
107   }
108 
109   auto throwable = env->ExceptionOccurred();
110   if (!throwable) {
111     throw std::runtime_error("Unable to get pending JNI exception.");
112   }
113   env->ExceptionClear();
114 
115   throw jni::JniException(jni::adopt_local(throwable));
116 }
117 
throwNewJavaException(jthrowable throwable)118 void throwNewJavaException(jthrowable throwable) {
119   throw jni::JniException(jni::wrap_alias(throwable));
120 }
121 
122 } // namespace expo
123