1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include <android/log.h>
13 #include "mozilla/Assertions.h"
14 #include "mozilla/java/GeckoAppShellWrappers.h"
15 #include "mozilla/java/GeckoThreadWrappers.h"
17 #include "AndroidBuild.h"
18 #include "nsAppShell.h"
19 #include "nsExceptionHandler.h"
26 #define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName, ABIName) \
28 constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call)( \
29 jobject, jmethodID, CallArgs::JValueType) MOZ_JNICALL_ABI; \
30 constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall)( \
31 jclass, jmethodID, CallArgs::JValueType) MOZ_JNICALL_ABI; \
32 constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get)(jobject, jfieldID) \
34 constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet)( \
35 jclass, jfieldID) ABIName; \
36 constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set)(jobject, jfieldID, \
38 constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet)( \
39 jclass, jfieldID, JNIType) ABIName; \
40 constexpr void (JNIEnv::*TypeAdapter<NativeType>::GetArray)( \
41 JNIType##Array, jsize, jsize, JNIType*)
43 DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean
, Boolean
, /*nothing*/);
44 DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte
, Byte
, /*nothing*/);
45 DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t
, jchar
, Char
, /*nothing*/);
46 DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort
, Short
, /*nothing*/);
47 DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint
, Int
, /*nothing*/);
48 DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t, jlong
, Long
, /*nothing*/);
49 DEFINE_PRIMITIVE_TYPE_ADAPTER(float, jfloat
, Float
, MOZ_JNICALL_ABI
);
50 DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble
, Double
, MOZ_JNICALL_ABI
);
52 #undef DEFINE_PRIMITIVE_TYPE_ADAPTER
57 const char ObjectBase
<Object
, jobject
>::name
[] = "java/lang/Object";
59 const char ObjectBase
<TypedObject
<jstring
>, jstring
>::name
[] =
62 const char ObjectBase
<TypedObject
<jclass
>, jclass
>::name
[] = "java/lang/Class";
64 const char ObjectBase
<TypedObject
<jthrowable
>, jthrowable
>::name
[] =
65 "java/lang/Throwable";
67 const char ObjectBase
<BoxedObject
<jboolean
>, jobject
>::name
[] =
70 const char ObjectBase
<BoxedObject
<jbyte
>, jobject
>::name
[] = "java/lang/Byte";
72 const char ObjectBase
<BoxedObject
<jchar
>, jobject
>::name
[] =
73 "java/lang/Character";
75 const char ObjectBase
<BoxedObject
<jshort
>, jobject
>::name
[] = "java/lang/Short";
77 const char ObjectBase
<BoxedObject
<jint
>, jobject
>::name
[] = "java/lang/Integer";
79 const char ObjectBase
<BoxedObject
<jlong
>, jobject
>::name
[] = "java/lang/Long";
81 const char ObjectBase
<BoxedObject
<jfloat
>, jobject
>::name
[] = "java/lang/Float";
83 const char ObjectBase
<BoxedObject
<jdouble
>, jobject
>::name
[] =
86 const char ObjectBase
<TypedObject
<jbooleanArray
>, jbooleanArray
>::name
[] = "[Z";
88 const char ObjectBase
<TypedObject
<jbyteArray
>, jbyteArray
>::name
[] = "[B";
90 const char ObjectBase
<TypedObject
<jcharArray
>, jcharArray
>::name
[] = "[C";
92 const char ObjectBase
<TypedObject
<jshortArray
>, jshortArray
>::name
[] = "[S";
94 const char ObjectBase
<TypedObject
<jintArray
>, jintArray
>::name
[] = "[I";
96 const char ObjectBase
<TypedObject
<jlongArray
>, jlongArray
>::name
[] = "[J";
98 const char ObjectBase
<TypedObject
<jfloatArray
>, jfloatArray
>::name
[] = "[F";
100 const char ObjectBase
<TypedObject
<jdoubleArray
>, jdoubleArray
>::name
[] = "[D";
102 const char ObjectBase
<TypedObject
<jobjectArray
>, jobjectArray
>::name
[] =
103 "[Ljava/lang/Object;";
105 const char ObjectBase
<ByteBuffer
, jobject
>::name
[] = "java/nio/ByteBuffer";
108 JNIEnv
* sGeckoThreadEnv
;
112 pthread_key_t sThreadEnvKey
;
113 jclass sOOMErrorClass
;
114 jobject sClassLoader
;
115 jmethodID sClassLoaderLoadClass
;
117 void UnregisterThreadEnv(void* env
) {
119 // We were never attached.
122 // The thread may have already been detached. In that case, it's still
123 // okay to call DetachCurrentThread(); it'll simply return an error.
124 // However, we must not access | env | because it may be invalid.
126 sJavaVM
->DetachCurrentThread();
131 void SetGeckoThreadEnv(JNIEnv
* aEnv
) {
133 MOZ_ASSERT(!sGeckoThreadEnv
|| sGeckoThreadEnv
== aEnv
);
135 if (!sGeckoThreadEnv
&&
136 pthread_key_create(&sThreadEnvKey
, UnregisterThreadEnv
)) {
137 MOZ_CRASH("Failed to initialize required TLS");
140 sGeckoThreadEnv
= aEnv
;
141 MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey
, aEnv
));
143 MOZ_ALWAYS_TRUE(!aEnv
->GetJavaVM(&sJavaVM
));
148 Class::LocalRef::Adopt(aEnv
->FindClass("java/lang/OutOfMemoryError")))
150 aEnv
->ExceptionClear();
152 sClassLoader
= Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
153 sClassLoaderLoadClass
= aEnv
->GetMethodID(
154 Class::LocalRef::Adopt(aEnv
->GetObjectClass(sClassLoader
)).Get(),
155 "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
156 MOZ_ASSERT(sClassLoader
&& sClassLoaderLoadClass
);
159 JNIEnv
* GetEnvForThread() {
160 MOZ_ASSERT(sGeckoThreadEnv
);
162 JNIEnv
* env
= static_cast<JNIEnv
*>(pthread_getspecific(sThreadEnvKey
));
167 // We don't have a saved JNIEnv, so try to get one.
168 // AttachCurrentThread() does the same thing as GetEnv() when a thread is
169 // already attached, so we don't have to call GetEnv() at all.
170 if (!sJavaVM
->AttachCurrentThread(&env
, nullptr)) {
172 MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey
, env
));
176 MOZ_CRASH("Failed to get JNIEnv for thread");
177 return nullptr; // unreachable
180 bool ThrowException(JNIEnv
* aEnv
, const char* aClass
, const char* aMessage
) {
181 MOZ_ASSERT(aEnv
, "Invalid thread JNI env");
183 Class::LocalRef cls
= Class::LocalRef::Adopt(aEnv
->FindClass(aClass
));
184 MOZ_ASSERT(cls
, "Cannot find exception class");
186 return !aEnv
->ThrowNew(cls
.Get(), aMessage
);
189 bool HandleUncaughtException(JNIEnv
* aEnv
) {
190 MOZ_ASSERT(aEnv
, "Invalid thread JNI env");
192 if (!aEnv
->ExceptionCheck()) {
197 aEnv
->ExceptionDescribe();
200 Throwable::LocalRef e
=
201 Throwable::LocalRef::Adopt(aEnv
, aEnv
->ExceptionOccurred());
203 aEnv
->ExceptionClear();
205 String::LocalRef stack
= java::GeckoAppShell::GetExceptionStackTrace(e
);
206 if (stack
&& ReportException(aEnv
, e
.Get(), stack
.Get())) {
210 aEnv
->ExceptionClear();
211 java::GeckoAppShell::HandleUncaughtException(e
);
213 if (NS_WARN_IF(aEnv
->ExceptionCheck())) {
214 aEnv
->ExceptionDescribe();
215 aEnv
->ExceptionClear();
221 bool ReportException(JNIEnv
* aEnv
, jthrowable aExc
, jstring aStack
) {
224 result
&= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
225 CrashReporter::Annotation::JavaStackTrace
,
226 String::Ref::From(aStack
)->ToCString()));
228 auto appNotes
= java::GeckoAppShell::GetAppNotes();
229 if (NS_WARN_IF(aEnv
->ExceptionCheck())) {
230 aEnv
->ExceptionDescribe();
231 aEnv
->ExceptionClear();
232 } else if (appNotes
) {
233 CrashReporter::AppendAppNotesToCrashReport("\n"_ns
+ appNotes
->ToCString());
236 if (sOOMErrorClass
&& aEnv
->IsInstanceOf(aExc
, sOOMErrorClass
)) {
237 NS_ABORT_OOM(0); // Unknown OOM size
244 jclass sJNIObjectClass
;
245 jfieldID sJNIObjectHandleField
;
247 bool EnsureJNIObject(JNIEnv
* env
, jobject instance
) {
248 if (!sJNIObjectClass
) {
250 Class::GlobalRef(Class::LocalRef::Adopt(GetClassRef(
251 env
, "org/mozilla/gecko/mozglue/JNIObject")))
254 sJNIObjectHandleField
= env
->GetFieldID(sJNIObjectClass
, "mHandle", "J");
257 MOZ_ASSERT(env
->IsInstanceOf(instance
, sJNIObjectClass
),
258 "Java class is not derived from JNIObject");
264 uintptr_t GetNativeHandle(JNIEnv
* env
, jobject instance
) {
265 if (!EnsureJNIObject(env
, instance
)) {
269 return static_cast<uintptr_t>(
270 env
->GetLongField(instance
, sJNIObjectHandleField
));
273 void SetNativeHandle(JNIEnv
* env
, jobject instance
, uintptr_t handle
) {
274 if (!EnsureJNIObject(env
, instance
)) {
278 env
->SetLongField(instance
, sJNIObjectHandleField
,
279 static_cast<jlong
>(handle
));
282 jclass
GetClassRef(JNIEnv
* aEnv
, const char* aClassName
) {
283 // First try the default class loader.
284 auto classRef
= Class::LocalRef::Adopt(aEnv
, aEnv
->FindClass(aClassName
));
286 if ((!classRef
|| aEnv
->ExceptionCheck()) && sClassLoader
) {
287 // If the default class loader failed but we have an app class loader, try
288 // that. Clear the pending exception from failed FindClass call above.
289 aEnv
->ExceptionClear();
290 classRef
= Class::LocalRef::Adopt(
292 jclass(aEnv
->CallObjectMethod(sClassLoader
, sClassLoaderLoadClass
,
293 StringParam(aClassName
, aEnv
).Get())));
296 if (classRef
&& !aEnv
->ExceptionCheck()) {
297 return classRef
.Forget();
301 ANDROID_LOG_ERROR
, "Gecko",
302 ">>> FATAL JNI ERROR! FindClass(\"%s\") failed. "
303 "Does the class require a newer API version? "
304 "Or did ProGuard optimize away something it shouldn't have?",
306 aEnv
->ExceptionDescribe();
307 MOZ_CRASH("Cannot find JNI class");
311 void DispatchToGeckoPriorityQueue(already_AddRefed
<nsIRunnable
> aCall
) {
312 class RunnableEvent
: public nsAppShell::Event
{
313 nsCOMPtr
<nsIRunnable
> mCall
;
316 explicit RunnableEvent(already_AddRefed
<nsIRunnable
> aCall
)
318 void Run() override
{ NS_ENSURE_SUCCESS_VOID(mCall
->Run()); }
321 nsAppShell::PostEvent(MakeUnique
<RunnableEvent
>(std::move(aCall
)));
324 int GetAPIVersion() {
325 static int32_t apiVersion
= 0;
326 if (!apiVersion
&& IsAvailable()) {
327 apiVersion
= java::sdk::Build::VERSION::SDK_INT();
332 pid_t
GetUIThreadId() {
333 static pid_t uiThreadId
;
335 uiThreadId
= pid_t(java::GeckoThread::UiThreadId());
340 bool IsOOMException(JNIEnv
* aEnv
) {
341 MOZ_ASSERT(aEnv
->ExceptionCheck());
342 Throwable::LocalRef e
=
343 Throwable::LocalRef::Adopt(aEnv
, aEnv
->ExceptionOccurred());
344 return sOOMErrorClass
&& aEnv
->IsInstanceOf(e
.Get(), sOOMErrorClass
);
348 } // namespace mozilla