1 #include "Utils.h"
2 #include "Types.h"
3 
4 #include <android/log.h>
5 #include <pthread.h>
6 
7 #include "mozilla/Assertions.h"
8 
9 #include "GeneratedJNIWrappers.h"
10 #include "nsAppShell.h"
11 
12 #ifdef MOZ_CRASHREPORTER
13 #include "nsExceptionHandler.h"
14 #endif
15 
16 namespace mozilla {
17 namespace jni {
18 
19 namespace detail {
20 
21 #define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName, ABIName) \
22     \
23     constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call) \
24             (jobject, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
25     constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall) \
26             (jclass, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
27     constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get) \
28             (jobject, jfieldID) ABIName; \
29     constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet) \
30             (jclass, jfieldID) ABIName; \
31     constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set) \
32             (jobject, jfieldID, JNIType) ABIName; \
33     constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet) \
34             (jclass, jfieldID, JNIType) ABIName; \
35     constexpr void (JNIEnv::*TypeAdapter<NativeType>::GetArray) \
36             (JNIType ## Array, jsize, jsize, JNIType*)
37 
38 DEFINE_PRIMITIVE_TYPE_ADAPTER(bool,     jboolean, Boolean, /*nothing*/);
39 DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t,   jbyte,    Byte,    /*nothing*/);
40 DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar,    Char,    /*nothing*/);
41 DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t,  jshort,   Short,   /*nothing*/);
42 DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t,  jint,     Int,     /*nothing*/);
43 DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t,  jlong,    Long,    /*nothing*/);
44 DEFINE_PRIMITIVE_TYPE_ADAPTER(float,    jfloat,   Float,   MOZ_JNICALL_ABI);
45 DEFINE_PRIMITIVE_TYPE_ADAPTER(double,   jdouble,  Double,  MOZ_JNICALL_ABI);
46 
47 #undef DEFINE_PRIMITIVE_TYPE_ADAPTER
48 
49 } // namespace detail
50 
51 template<> const char ObjectBase<Object, jobject>::name[] = "java/lang/Object";
52 template<> const char ObjectBase<TypedObject<jstring>, jstring>::name[] = "java/lang/String";
53 template<> const char ObjectBase<TypedObject<jclass>, jclass>::name[] = "java/lang/Class";
54 template<> const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[] = "java/lang/Throwable";
55 template<> const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[] = "[Z";
56 template<> const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[] = "[B";
57 template<> const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[] = "[C";
58 template<> const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[] = "[S";
59 template<> const char ObjectBase<TypedObject<jintArray>, jintArray>::name[] = "[I";
60 template<> const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[] = "[J";
61 template<> const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[] = "[F";
62 template<> const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[] = "[D";
63 template<> const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[] = "[Ljava/lang/Object;";
64 template<> const char ObjectBase<ByteBuffer, jobject>::name[] = "java/nio/ByteBuffer";
65 
66 
67 JNIEnv* sGeckoThreadEnv;
68 
69 namespace {
70 
71 JavaVM* sJavaVM;
72 pthread_key_t sThreadEnvKey;
73 jclass sOOMErrorClass;
74 jobject sClassLoader;
75 jmethodID sClassLoaderLoadClass;
76 bool sIsFennec;
77 
UnregisterThreadEnv(void * env)78 void UnregisterThreadEnv(void* env)
79 {
80     if (!env) {
81         // We were never attached.
82         return;
83     }
84     // The thread may have already been detached. In that case, it's still
85     // okay to call DetachCurrentThread(); it'll simply return an error.
86     // However, we must not access | env | because it may be invalid.
87     MOZ_ASSERT(sJavaVM);
88     sJavaVM->DetachCurrentThread();
89 }
90 
91 } // namespace
92 
SetGeckoThreadEnv(JNIEnv * aEnv)93 void SetGeckoThreadEnv(JNIEnv* aEnv)
94 {
95     MOZ_ASSERT(aEnv);
96     MOZ_ASSERT(!sGeckoThreadEnv || sGeckoThreadEnv == aEnv);
97 
98     if (!sGeckoThreadEnv
99             && pthread_key_create(&sThreadEnvKey, UnregisterThreadEnv)) {
100         MOZ_CRASH("Failed to initialize required TLS");
101     }
102 
103     sGeckoThreadEnv = aEnv;
104     MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, aEnv));
105 
106     MOZ_ALWAYS_TRUE(!aEnv->GetJavaVM(&sJavaVM));
107     MOZ_ASSERT(sJavaVM);
108 
109     sOOMErrorClass = Class::GlobalRef(Class::LocalRef::Adopt(
110             aEnv->FindClass("java/lang/OutOfMemoryError"))).Forget();
111     aEnv->ExceptionClear();
112 
113     sClassLoader = Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
114     sClassLoaderLoadClass = aEnv->GetMethodID(
115             Class::LocalRef::Adopt(aEnv->GetObjectClass(sClassLoader)).Get(),
116             "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
117     MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
118 
119     auto geckoAppClass = Class::LocalRef::Adopt(
120             aEnv->FindClass("org/mozilla/gecko/GeckoApp"));
121     aEnv->ExceptionClear();
122     sIsFennec = !!geckoAppClass;
123 }
124 
GetEnvForThread()125 JNIEnv* GetEnvForThread()
126 {
127     MOZ_ASSERT(sGeckoThreadEnv);
128 
129     JNIEnv* env = static_cast<JNIEnv*>(pthread_getspecific(sThreadEnvKey));
130     if (env) {
131         return env;
132     }
133 
134     // We don't have a saved JNIEnv, so try to get one.
135     // AttachCurrentThread() does the same thing as GetEnv() when a thread is
136     // already attached, so we don't have to call GetEnv() at all.
137     if (!sJavaVM->AttachCurrentThread(&env, nullptr)) {
138         MOZ_ASSERT(env);
139         MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env));
140         return env;
141     }
142 
143     MOZ_CRASH("Failed to get JNIEnv for thread");
144     return nullptr; // unreachable
145 }
146 
ThrowException(JNIEnv * aEnv,const char * aClass,const char * aMessage)147 bool ThrowException(JNIEnv *aEnv, const char *aClass,
148                     const char *aMessage)
149 {
150     MOZ_ASSERT(aEnv, "Invalid thread JNI env");
151 
152     Class::LocalRef cls = Class::LocalRef::Adopt(aEnv->FindClass(aClass));
153     MOZ_ASSERT(cls, "Cannot find exception class");
154 
155     return !aEnv->ThrowNew(cls.Get(), aMessage);
156 }
157 
HandleUncaughtException(JNIEnv * aEnv)158 bool HandleUncaughtException(JNIEnv* aEnv)
159 {
160     MOZ_ASSERT(aEnv, "Invalid thread JNI env");
161 
162     if (!aEnv->ExceptionCheck()) {
163         return false;
164     }
165 
166 #ifdef MOZ_CHECK_JNI
167     aEnv->ExceptionDescribe();
168 #endif
169 
170     Throwable::LocalRef e =
171             Throwable::LocalRef::Adopt(aEnv, aEnv->ExceptionOccurred());
172     MOZ_ASSERT(e);
173     aEnv->ExceptionClear();
174 
175     String::LocalRef stack = java::GeckoAppShell::GetExceptionStackTrace(e);
176     if (stack && ReportException(aEnv, e.Get(), stack.Get())) {
177         return true;
178     }
179 
180     aEnv->ExceptionClear();
181     java::GeckoAppShell::HandleUncaughtException(e);
182 
183     if (NS_WARN_IF(aEnv->ExceptionCheck())) {
184         aEnv->ExceptionDescribe();
185         aEnv->ExceptionClear();
186     }
187 
188     return true;
189 }
190 
ReportException(JNIEnv * aEnv,jthrowable aExc,jstring aStack)191 bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack)
192 {
193     bool result = true;
194 
195 #ifdef MOZ_CRASHREPORTER
196     result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
197             NS_LITERAL_CSTRING("JavaStackTrace"),
198             String::Ref::From(aStack)->ToCString()));
199 #endif // MOZ_CRASHREPORTER
200 
201     if (sOOMErrorClass && aEnv->IsInstanceOf(aExc, sOOMErrorClass)) {
202         NS_ABORT_OOM(0); // Unknown OOM size
203     }
204     return result;
205 }
206 
207 namespace {
208 
209 jclass sJNIObjectClass;
210 jfieldID sJNIObjectHandleField;
211 
EnsureJNIObject(JNIEnv * env,jobject instance)212 bool EnsureJNIObject(JNIEnv* env, jobject instance) {
213     if (!sJNIObjectClass) {
214         sJNIObjectClass = Class::GlobalRef(Class::LocalRef::Adopt(GetClassRef(
215                 env, "org/mozilla/gecko/mozglue/JNIObject"))).Forget();
216 
217         sJNIObjectHandleField = env->GetFieldID(
218                 sJNIObjectClass, "mHandle", "J");
219     }
220 
221     MOZ_ASSERT(env->IsInstanceOf(instance, sJNIObjectClass));
222     return true;
223 }
224 
225 } // namespace
226 
GetNativeHandle(JNIEnv * env,jobject instance)227 uintptr_t GetNativeHandle(JNIEnv* env, jobject instance)
228 {
229     if (!EnsureJNIObject(env, instance)) {
230         return 0;
231     }
232 
233     return static_cast<uintptr_t>(
234             env->GetLongField(instance, sJNIObjectHandleField));
235 }
236 
SetNativeHandle(JNIEnv * env,jobject instance,uintptr_t handle)237 void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle)
238 {
239     if (!EnsureJNIObject(env, instance)) {
240         return;
241     }
242 
243     env->SetLongField(instance, sJNIObjectHandleField,
244                       static_cast<jlong>(handle));
245 }
246 
GetClassRef(JNIEnv * aEnv,const char * aClassName)247 jclass GetClassRef(JNIEnv* aEnv, const char* aClassName)
248 {
249     // First try the default class loader.
250     auto classRef = Class::LocalRef::Adopt(aEnv, aEnv->FindClass(aClassName));
251 
252     if (!classRef && sClassLoader) {
253         // If the default class loader failed but we have an app class loader, try that.
254         // Clear the pending exception from failed FindClass call above.
255         aEnv->ExceptionClear();
256         classRef = Class::LocalRef::Adopt(aEnv, jclass(
257                 aEnv->CallObjectMethod(sClassLoader, sClassLoaderLoadClass,
258                                        StringParam(aClassName, aEnv).Get())));
259     }
260 
261     if (classRef) {
262         return classRef.Forget();
263     }
264 
265     __android_log_print(
266             ANDROID_LOG_ERROR, "Gecko",
267             ">>> FATAL JNI ERROR! FindClass(className=\"%s\") failed. "
268             "Did ProGuard optimize away something it shouldn't have?",
269             aClassName);
270     aEnv->ExceptionDescribe();
271     MOZ_CRASH("Cannot find JNI class");
272     return nullptr;
273 }
274 
DispatchToGeckoThread(UniquePtr<AbstractCall> && aCall)275 void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall)
276 {
277     class AbstractCallEvent : public nsAppShell::Event
278     {
279         UniquePtr<AbstractCall> mCall;
280 
281     public:
282         AbstractCallEvent(UniquePtr<AbstractCall>&& aCall)
283             : mCall(Move(aCall))
284         {}
285 
286         void Run() override
287         {
288             (*mCall)();
289         }
290     };
291 
292     nsAppShell::PostEvent(MakeUnique<AbstractCallEvent>(Move(aCall)));
293 }
294 
IsFennec()295 bool IsFennec()
296 {
297     return sIsFennec;
298 }
299 
300 } // jni
301 } // mozilla
302