summaryrefslogtreecommitdiffstats
path: root/widget/android/jni/Refs.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /widget/android/jni/Refs.h
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'widget/android/jni/Refs.h')
-rw-r--r--widget/android/jni/Refs.h1117
1 files changed, 1117 insertions, 0 deletions
diff --git a/widget/android/jni/Refs.h b/widget/android/jni/Refs.h
new file mode 100644
index 0000000000..9b50703491
--- /dev/null
+++ b/widget/android/jni/Refs.h
@@ -0,0 +1,1117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_jni_Refs_h__
+#define mozilla_jni_Refs_h__
+
+#include <jni.h>
+
+#include <utility>
+
+#include "mozilla/fallible.h"
+#include "mozilla/jni/Utils.h"
+#include "mozilla/jni/TypeAdapter.h"
+#include "nsError.h" // for nsresult
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace jni {
+
+// Wrapped object reference (e.g. jobject, jclass, etc...)
+template <class Cls, typename JNIType>
+class Ref;
+// Represents a calling context for JNI methods.
+template <class Cls, typename JNIType>
+class Context;
+// Wrapped local reference that inherits from Ref.
+template <class Cls>
+class LocalRef;
+// Wrapped global reference that inherits from Ref.
+template <class Cls>
+class GlobalRef;
+// Wrapped weak reference that inherits from Ref.
+template <class Cls>
+class WeakRef;
+// Wrapped dangling reference that's owned by someone else.
+template <class Cls>
+class DependentRef;
+
+// Class to hold the native types of a method's arguments.
+// For example, if a method has signature (ILjava/lang/String;)V,
+// its arguments class would be jni::Args<int32_t, jni::String::Param>
+template <typename...>
+struct Args {};
+
+class Object;
+
+// Base class for Ref and its specializations.
+template <class Cls, typename Type>
+class Ref {
+ template <class C, typename T>
+ friend class Ref;
+
+ using Self = Ref<Cls, Type>;
+ using bool_type = void (Self::*)() const;
+ void non_null_reference() const {}
+
+ // A Cls-derivative that allows copying
+ // (e.g. when acting as a return value).
+ struct CopyableCtx : public Context<Cls, Type> {
+ CopyableCtx(JNIEnv* env, Type instance)
+ : Context<Cls, Type>(env, instance) {}
+
+ CopyableCtx(const CopyableCtx& cls)
+ : Context<Cls, Type>(cls.Env(), cls.Get()) {}
+ };
+
+ // Private copy constructor so that there's no danger of assigning a
+ // temporary LocalRef/GlobalRef to a Ref, and potentially use the Ref
+ // after the source had been freed.
+ Ref(const Ref&) = default;
+
+ protected:
+ static JNIEnv* FindEnv() {
+ return Cls::callingThread == CallingThread::GECKO ? GetGeckoThreadEnv()
+ : GetEnvForThread();
+ }
+
+ Type mInstance;
+
+ // Protected jobject constructor because outside code should be using
+ // Ref::From. Using Ref::From makes it very easy to see which code is using
+ // raw JNI types for future refactoring.
+ explicit Ref(Type instance) : mInstance(instance) {}
+
+ public:
+ using JNIType = Type;
+
+ class AutoLock {
+ friend class Ref<Cls, Type>;
+
+ JNIEnv* const mEnv;
+ Type mInstance;
+
+ explicit AutoLock(Type aInstance)
+ : mEnv(FindEnv()), mInstance(mEnv->NewLocalRef(aInstance)) {
+ mEnv->MonitorEnter(mInstance);
+ MOZ_CATCH_JNI_EXCEPTION(mEnv);
+ }
+
+ public:
+ AutoLock(AutoLock&& aOther)
+ : mEnv(aOther.mEnv), mInstance(aOther.mInstance) {
+ aOther.mInstance = nullptr;
+ }
+
+ ~AutoLock() { Unlock(); }
+
+ void Unlock() {
+ if (mInstance) {
+ mEnv->MonitorExit(mInstance);
+ mEnv->DeleteLocalRef(mInstance);
+ MOZ_CATCH_JNI_EXCEPTION(mEnv);
+ mInstance = nullptr;
+ }
+ }
+ };
+
+ // Construct a Ref form a raw JNI reference.
+ static Ref<Cls, Type> From(JNIType obj) { return Ref<Cls, Type>(obj); }
+
+ // Construct a Ref form a generic object reference.
+ static Ref<Cls, Type> From(const Ref<Object, jobject>& obj) {
+ return Ref<Cls, Type>(JNIType(obj.Get()));
+ }
+
+ MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {}
+
+ // Get the raw JNI reference.
+ JNIType Get() const { return mInstance; }
+
+ template <class T>
+ bool IsInstanceOf() const {
+ return FindEnv()->IsInstanceOf(mInstance, typename T::Context().ClassRef());
+ }
+
+ template <class T>
+ typename T::Ref Cast() const {
+#ifdef MOZ_CHECK_JNI
+ MOZ_RELEASE_ASSERT(FindEnv()->IsAssignableFrom(
+ Context<Cls, Type>().ClassRef(), typename T::Context().ClassRef()));
+#endif
+ return T::Ref::From(*this);
+ }
+
+ AutoLock Lock() const { return AutoLock(mInstance); }
+
+ bool operator==(const Ref& other) const {
+ // Treat two references of the same object as being the same.
+ return mInstance == other.mInstance ||
+ JNI_FALSE != FindEnv()->IsSameObject(mInstance, other.mInstance);
+ }
+
+ bool operator!=(const Ref& other) const { return !operator==(other); }
+
+ bool operator==(decltype(nullptr)) const { return !mInstance; }
+
+ bool operator!=(decltype(nullptr)) const { return !!mInstance; }
+
+ CopyableCtx operator->() const { return CopyableCtx(FindEnv(), mInstance); }
+
+ CopyableCtx operator*() const { return operator->(); }
+
+ // Any ref can be cast to an object ref.
+ operator Ref<Object, jobject>() const {
+ return Ref<Object, jobject>(mInstance);
+ }
+
+ // Null checking (e.g. !!ref) using the safe-bool idiom.
+ operator bool_type() const {
+ return mInstance ? &Self::non_null_reference : nullptr;
+ }
+
+ // We don't allow implicit conversion to jobject because that can lead
+ // to easy mistakes such as assigning a temporary LocalRef to a jobject,
+ // and using the jobject after the LocalRef has been freed.
+
+ // We don't allow explicit conversion, to make outside code use Ref::Get.
+ // Using Ref::Get makes it very easy to see which code is using raw JNI
+ // types to make future refactoring easier.
+
+ // operator JNIType() const = delete;
+};
+
+// Represents a calling context for JNI methods.
+template <class Cls, typename Type>
+class Context : public Ref<Cls, Type> {
+ using Ref = jni::Ref<Cls, Type>;
+
+ static jclass sClassRef; // global reference
+
+ protected:
+ JNIEnv* const mEnv;
+
+ public:
+ Context() : Ref(nullptr), mEnv(Ref::FindEnv()) {}
+
+ Context(JNIEnv* env, Type instance) : Ref(instance), mEnv(env) {}
+
+ jclass ClassRef() const {
+ if (!sClassRef) {
+ const jclass cls = GetClassRef(mEnv, Cls::name);
+ sClassRef = jclass(mEnv->NewGlobalRef(cls));
+ mEnv->DeleteLocalRef(cls);
+ }
+ return sClassRef;
+ }
+
+ JNIEnv* Env() const { return mEnv; }
+
+ template <class T>
+ bool IsInstanceOf() const {
+ return mEnv->IsInstanceOf(Ref::mInstance,
+ typename T::Context(mEnv, nullptr).ClassRef());
+ }
+
+ bool operator==(const Ref& other) const {
+ // Treat two references of the same object as being the same.
+ return Ref::mInstance == other.Get() ||
+ JNI_FALSE != mEnv->IsSameObject(Ref::mInstance, other.Get());
+ }
+
+ bool operator!=(const Ref& other) const { return !operator==(other); }
+
+ bool operator==(decltype(nullptr)) const { return !Ref::mInstance; }
+
+ bool operator!=(decltype(nullptr)) const { return !!Ref::mInstance; }
+
+ Cls operator->() const {
+ MOZ_ASSERT(Ref::mInstance, "Null jobject");
+ return Cls(*this);
+ }
+
+ const Context<Cls, Type>& operator*() const { return *this; }
+};
+
+template <class C, typename T>
+jclass Context<C, T>::sClassRef;
+
+template <class Cls, typename Type = jobject>
+class ObjectBase {
+ protected:
+ const jni::Context<Cls, Type>& mCtx;
+
+ jclass ClassRef() const { return mCtx.ClassRef(); }
+ JNIEnv* Env() const { return mCtx.Env(); }
+ Type Instance() const { return mCtx.Get(); }
+
+ public:
+ using Ref = jni::Ref<Cls, Type>;
+ using Context = jni::Context<Cls, Type>;
+ using LocalRef = jni::LocalRef<Cls>;
+ using GlobalRef = jni::GlobalRef<Cls>;
+ using WeakRef = jni::WeakRef<Cls>;
+ using Param = const Ref&;
+
+ static const CallingThread callingThread = CallingThread::ANY;
+ static const char name[];
+
+ explicit ObjectBase(const Context& ctx) : mCtx(ctx) {}
+
+ Cls* operator->() { return static_cast<Cls*>(this); }
+};
+
+// Binding for a plain jobject.
+class Object : public ObjectBase<Object, jobject> {
+ public:
+ explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {}
+};
+
+// Binding for a built-in object reference other than jobject.
+template <typename T>
+class TypedObject : public ObjectBase<TypedObject<T>, T> {
+ public:
+ explicit TypedObject(const Context<TypedObject<T>, T>& ctx)
+ : ObjectBase<TypedObject<T>, T>(ctx) {}
+};
+
+// Binding for a boxed primitive object.
+template <typename T>
+class BoxedObject : public ObjectBase<BoxedObject<T>, jobject> {
+ public:
+ explicit BoxedObject(const Context<BoxedObject<T>, jobject>& ctx)
+ : ObjectBase<BoxedObject<T>, jobject>(ctx) {}
+};
+
+template <>
+const char ObjectBase<Object, jobject>::name[];
+template <>
+const char ObjectBase<TypedObject<jstring>, jstring>::name[];
+template <>
+const char ObjectBase<TypedObject<jclass>, jclass>::name[];
+template <>
+const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[];
+template <>
+const char ObjectBase<BoxedObject<jboolean>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jbyte>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jchar>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jshort>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jint>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jlong>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jfloat>, jobject>::name[];
+template <>
+const char ObjectBase<BoxedObject<jdouble>, jobject>::name[];
+template <>
+const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jintArray>, jintArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[];
+template <>
+const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[];
+
+// Define bindings for built-in types.
+using String = TypedObject<jstring>;
+using Class = TypedObject<jclass>;
+using Throwable = TypedObject<jthrowable>;
+
+using Boolean = BoxedObject<jboolean>;
+using Byte = BoxedObject<jbyte>;
+using Character = BoxedObject<jchar>;
+using Short = BoxedObject<jshort>;
+using Integer = BoxedObject<jint>;
+using Long = BoxedObject<jlong>;
+using Float = BoxedObject<jfloat>;
+using Double = BoxedObject<jdouble>;
+
+using BooleanArray = TypedObject<jbooleanArray>;
+using ByteArray = TypedObject<jbyteArray>;
+using CharArray = TypedObject<jcharArray>;
+using ShortArray = TypedObject<jshortArray>;
+using IntArray = TypedObject<jintArray>;
+using LongArray = TypedObject<jlongArray>;
+using FloatArray = TypedObject<jfloatArray>;
+using DoubleArray = TypedObject<jdoubleArray>;
+using ObjectArray = TypedObject<jobjectArray>;
+
+namespace detail {
+
+// See explanation in LocalRef.
+template <class Cls>
+struct GenericObject {
+ using Type = Object;
+};
+template <>
+struct GenericObject<Object> {
+ struct Type {
+ using Ref = jni::Ref<Type, jobject>;
+ using Context = jni::Context<Type, jobject>;
+ };
+};
+template <class Cls>
+struct GenericLocalRef {
+ template <class C>
+ struct Type : jni::Object {};
+};
+template <>
+struct GenericLocalRef<Object> {
+ template <class C>
+ using Type = jni::LocalRef<C>;
+};
+
+} // namespace detail
+
+template <class Cls>
+class LocalRef : public Cls::Context {
+ template <class C>
+ friend class LocalRef;
+
+ using Ctx = typename Cls::Context;
+ using Ref = typename Cls::Ref;
+ using JNIType = typename Ref::JNIType;
+
+ // In order to be able to convert LocalRef<Object> to LocalRef<Cls>, we
+ // need constructors and copy assignment operators that take in a
+ // LocalRef<Object> argument. However, if Cls *is* Object, we would have
+ // duplicated constructors and operators with LocalRef<Object> arguments. To
+ // avoid this conflict, we use GenericObject, which is defined as Object for
+ // LocalRef<non-Object> and defined as a dummy class for LocalRef<Object>.
+ using GenericObject = typename detail::GenericObject<Cls>::Type;
+
+ // Similarly, GenericLocalRef is useed to convert LocalRef<Cls> to,
+ // LocalRef<Object>. It's defined as LocalRef<C> for Cls == Object,
+ // and defined as a dummy template class for Cls != Object.
+ template <class C>
+ using GenericLocalRef =
+ typename detail::GenericLocalRef<Cls>::template Type<C>;
+
+ static JNIType NewLocalRef(JNIEnv* env, JNIType obj) {
+ return JNIType(obj ? env->NewLocalRef(obj) : nullptr);
+ }
+
+ LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {}
+
+ LocalRef& swap(LocalRef& other) {
+ auto instance = other.mInstance;
+ other.mInstance = Ctx::mInstance;
+ Ctx::mInstance = instance;
+ return *this;
+ }
+
+ public:
+ // Construct a LocalRef from a raw JNI local reference. Unlike Ref::From,
+ // LocalRef::Adopt returns a LocalRef that will delete the local reference
+ // when going out of scope.
+ static LocalRef Adopt(JNIType instance) {
+ return LocalRef(Ref::FindEnv(), instance);
+ }
+
+ static LocalRef Adopt(JNIEnv* env, JNIType instance) {
+ return LocalRef(env, instance);
+ }
+
+ // Copy constructor.
+ LocalRef(const LocalRef<Cls>& ref)
+ : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance)) {}
+
+ // Move constructor.
+ LocalRef(LocalRef<Cls>&& ref) : Ctx(ref.mEnv, ref.mInstance) {
+ ref.mInstance = nullptr;
+ }
+
+ explicit LocalRef(JNIEnv* env = Ref::FindEnv()) : Ctx(env, nullptr) {}
+
+ // Construct a LocalRef from any Ref,
+ // which means creating a new local reference.
+ MOZ_IMPLICIT LocalRef(const Ref& ref) : Ctx(Ref::FindEnv(), nullptr) {
+ Ctx::mInstance = NewLocalRef(Ctx::mEnv, ref.Get());
+ }
+
+ LocalRef(JNIEnv* env, const Ref& ref)
+ : Ctx(env, NewLocalRef(env, ref.Get())) {}
+
+ // Move a LocalRef<Object> into a LocalRef<Cls> without
+ // creating/deleting local references.
+ MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
+ : Ctx(ref.mEnv, JNIType(ref.mInstance)) {
+ ref.mInstance = nullptr;
+ }
+
+ template <class C>
+ MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
+ : Ctx(ref.mEnv, ref.mInstance) {
+ ref.mInstance = nullptr;
+ }
+
+ // Implicitly converts nullptr to LocalRef.
+ MOZ_IMPLICIT LocalRef(decltype(nullptr)) : Ctx(Ref::FindEnv(), nullptr) {}
+
+ ~LocalRef() {
+ if (Ctx::mInstance) {
+ Ctx::mEnv->DeleteLocalRef(Ctx::mInstance);
+ Ctx::mInstance = nullptr;
+ }
+ }
+
+ // Get the raw JNI reference that can be used as a return value.
+ // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
+ typename Ref::JNIType Forget() {
+ const auto obj = Ctx::Get();
+ Ctx::mInstance = nullptr;
+ return obj;
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<Cls> ref) & { return swap(ref); }
+
+ LocalRef<Cls>& operator=(const Ref& ref) & {
+ LocalRef<Cls> newRef(Ctx::mEnv, ref);
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref) & {
+ LocalRef<Cls> newRef(std::move(ref));
+ return swap(newRef);
+ }
+
+ template <class C>
+ LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref) & {
+ LocalRef<Cls> newRef(std::move(ref));
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(decltype(nullptr)) & {
+ LocalRef<Cls> newRef(Ctx::mEnv, nullptr);
+ return swap(newRef);
+ }
+};
+
+template <class Cls>
+class GlobalRef : public Cls::Ref {
+ using Ref = typename Cls::Ref;
+ using JNIType = typename Ref::JNIType;
+
+ static JNIType NewGlobalRef(JNIEnv* env, JNIType instance) {
+ return JNIType(instance ? env->NewGlobalRef(instance) : nullptr);
+ }
+
+ GlobalRef& swap(GlobalRef& other) {
+ auto instance = other.mInstance;
+ other.mInstance = Ref::mInstance;
+ Ref::mInstance = instance;
+ return *this;
+ }
+
+ public:
+ GlobalRef() : Ref(nullptr) {}
+
+ // Copy constructor
+ GlobalRef(const GlobalRef& ref)
+ : Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance)) {}
+
+ // Move constructor
+ GlobalRef(GlobalRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
+
+ MOZ_IMPLICIT GlobalRef(const Ref& ref)
+ : Ref(NewGlobalRef(GetEnvForThread(), ref.Get())) {}
+
+ GlobalRef(JNIEnv* env, const Ref& ref) : Ref(NewGlobalRef(env, ref.Get())) {}
+
+ MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref)
+ : Ref(NewGlobalRef(ref.Env(), ref.Get())) {}
+
+ // Implicitly converts nullptr to GlobalRef.
+ MOZ_IMPLICIT GlobalRef(decltype(nullptr)) : Ref(nullptr) {}
+
+ ~GlobalRef() {
+ if (Ref::mInstance) {
+ Clear(GetEnvForThread());
+ }
+ }
+
+ // Get the raw JNI reference that can be used as a return value.
+ // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
+ typename Ref::JNIType Forget() {
+ const auto obj = Ref::Get();
+ Ref::mInstance = nullptr;
+ return obj;
+ }
+
+ void Clear(JNIEnv* env) {
+ if (Ref::mInstance) {
+ env->DeleteGlobalRef(Ref::mInstance);
+ Ref::mInstance = nullptr;
+ }
+ }
+
+ GlobalRef<Cls>& operator=(GlobalRef<Cls> ref) & { return swap(ref); }
+
+ GlobalRef<Cls>& operator=(const Ref& ref) & {
+ GlobalRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
+ GlobalRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ GlobalRef<Cls>& operator=(decltype(nullptr)) & {
+ GlobalRef<Cls> newRef(nullptr);
+ return swap(newRef);
+ }
+};
+
+template <class Cls>
+class WeakRef : public Ref<Cls, jweak> {
+ using Ref = Ref<Cls, jweak>;
+ using JNIType = typename Ref::JNIType;
+
+ static JNIType NewWeakRef(JNIEnv* env, JNIType instance) {
+ return JNIType(instance ? env->NewWeakGlobalRef(instance) : nullptr);
+ }
+
+ WeakRef& swap(WeakRef& other) {
+ auto instance = other.mInstance;
+ other.mInstance = Ref::mInstance;
+ Ref::mInstance = instance;
+ return *this;
+ }
+
+ public:
+ WeakRef() : Ref(nullptr) {}
+
+ // Copy constructor
+ WeakRef(const WeakRef& ref)
+ : Ref(NewWeakRef(GetEnvForThread(), ref.mInstance)) {}
+
+ // Move constructor
+ WeakRef(WeakRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
+
+ MOZ_IMPLICIT WeakRef(const Ref& ref)
+ : Ref(NewWeakRef(GetEnvForThread(), ref.Get())) {}
+
+ WeakRef(JNIEnv* env, const Ref& ref) : Ref(NewWeakRef(env, ref.Get())) {}
+
+ MOZ_IMPLICIT WeakRef(const LocalRef<Cls>& ref)
+ : Ref(NewWeakRef(ref.Env(), ref.Get())) {}
+
+ // Implicitly converts nullptr to WeakRef.
+ MOZ_IMPLICIT WeakRef(decltype(nullptr)) : Ref(nullptr) {}
+
+ ~WeakRef() {
+ if (Ref::mInstance) {
+ Clear(GetEnvForThread());
+ }
+ }
+
+ // Get the raw JNI reference that can be used as a return value.
+ // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
+ typename Ref::JNIType Forget() {
+ const auto obj = Ref::Get();
+ Ref::mInstance = nullptr;
+ return obj;
+ }
+
+ void Clear(JNIEnv* env) {
+ if (Ref::mInstance) {
+ env->DeleteWeakGlobalRef(Ref::mInstance);
+ Ref::mInstance = nullptr;
+ }
+ }
+
+ WeakRef<Cls>& operator=(WeakRef<Cls> ref) & { return swap(ref); }
+
+ WeakRef<Cls>& operator=(const Ref& ref) & {
+ WeakRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ WeakRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
+ WeakRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ WeakRef<Cls>& operator=(decltype(nullptr)) & {
+ WeakRef<Cls> newRef(nullptr);
+ return swap(newRef);
+ }
+
+ void operator->() const = delete;
+ void operator*() const = delete;
+};
+
+template <class Cls>
+class DependentRef : public Cls::Ref {
+ using Ref = typename Cls::Ref;
+
+ public:
+ explicit DependentRef(typename Ref::JNIType instance) : Ref(instance) {}
+
+ DependentRef(const DependentRef& ref) : Ref(ref.Get()) {}
+};
+
+class StringParam;
+
+template <>
+class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring> {
+ using Base = ObjectBase<TypedObject<jstring>, jstring>;
+
+ public:
+ using Param = const StringParam&;
+
+ explicit TypedObject(const Context& ctx) : Base(ctx) {}
+
+ size_t Length() const {
+ const size_t ret = Base::Env()->GetStringLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsString ToString() const {
+ const jchar* const str =
+ Base::Env()->GetStringChars(Base::Instance(), nullptr);
+ const jsize len = Base::Env()->GetStringLength(Base::Instance());
+
+ nsString result(reinterpret_cast<const char16_t*>(str), len);
+ Base::Env()->ReleaseStringChars(Base::Instance(), str);
+ return result;
+ }
+
+ nsCString ToCString() const { return NS_ConvertUTF16toUTF8(ToString()); }
+
+ // Convert jstring to a nsString.
+ operator nsString() const { return ToString(); }
+
+ // Convert jstring to a nsCString.
+ operator nsCString() const { return ToCString(); }
+};
+
+// Define a custom parameter type for String,
+// which accepts both String::Ref and nsAString/nsACString
+class StringParam : public String::Ref {
+ using Ref = String::Ref;
+
+ private:
+ // Not null if we should delete ref on destruction.
+ JNIEnv* const mEnv;
+
+ static jstring GetString(JNIEnv* env, const nsAString& str) {
+ const jstring result = env->NewString(
+ reinterpret_cast<const jchar*>(str.BeginReading()), str.Length());
+ if (!result) {
+ NS_ABORT_OOM(str.Length() * sizeof(char16_t));
+ }
+ MOZ_CATCH_JNI_EXCEPTION(env);
+ return result;
+ }
+
+ static jstring GetString(JNIEnv* env, const nsAString& str,
+ const fallible_t&) {
+ const jstring result = env->NewString(
+ reinterpret_cast<const jchar*>(str.BeginReading()), str.Length());
+ if (env->ExceptionCheck()) {
+#ifdef MOZ_CHECK_JNI
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+ return result;
+ }
+
+ static jstring GetString(JNIEnv* env, const nsACString& str,
+ const fallible_t& aFallible) {
+ nsAutoString utf16;
+ if (!CopyUTF8toUTF16(str, utf16, aFallible)) {
+ return nullptr;
+ }
+ return GetString(env, utf16, aFallible);
+ }
+
+ public:
+ MOZ_IMPLICIT StringParam(decltype(nullptr)) : Ref(nullptr), mEnv(nullptr) {}
+
+ MOZ_IMPLICIT StringParam(const Ref& ref) : Ref(ref.Get()), mEnv(nullptr) {}
+
+ MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, str)), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env,
+ const fallible_t& aFallible)
+ : Ref(GetString(env, str, aFallible)), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const nsLiteralString& str,
+ JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, str)), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, nsDependentString(str))), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env,
+ const fallible_t& aFallible)
+ : Ref(GetString(env, str, aFallible)), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const nsLiteralCString& str,
+ JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
+
+ MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
+
+ StringParam(StringParam&& other) : Ref(other.Get()), mEnv(other.mEnv) {
+ other.mInstance = nullptr;
+ }
+
+ ~StringParam() {
+ if (mEnv && Get()) {
+ mEnv->DeleteLocalRef(Get());
+ }
+ }
+
+ operator String::LocalRef() const {
+ // We can't return our existing ref because the returned
+ // LocalRef could be freed first, so we need a new local ref.
+ return String::LocalRef(mEnv ? mEnv : Ref::FindEnv(), *this);
+ }
+};
+
+namespace detail {
+template <typename T>
+struct TypeAdapter;
+}
+
+// Ref specialization for arrays.
+template <typename JNIType, class ElementType>
+class ArrayRefBase : public ObjectBase<TypedObject<JNIType>, JNIType> {
+ protected:
+ using Base = ObjectBase<TypedObject<JNIType>, JNIType>;
+
+ public:
+ explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx)
+ : Base(ctx) {}
+
+ static typename Base::LocalRef New(const ElementType* data, size_t length) {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+ auto result = (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ (jenv->*detail::TypeAdapter<ElementType>::SetArray)(
+ result, jsize(0), length, reinterpret_cast<const JNIElemType*>(data));
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ return Base::LocalRef::Adopt(jenv, result);
+ }
+
+ static typename Base::LocalRef New(const ElementType* data, size_t length,
+ const fallible_t&) {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+ auto result = (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
+ if (jenv->ExceptionCheck()) {
+ if (!IsOOMException(jenv)) {
+ // This exception isn't excepted due not to OOM. This is unrecoverable
+ // error.
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ }
+#ifdef MOZ_CHECK_JNI
+ jenv->ExceptionDescribe();
+#endif
+ jenv->ExceptionClear();
+ return Base::LocalRef::Adopt(jenv, nullptr);
+ }
+ (jenv->*detail::TypeAdapter<ElementType>::SetArray)(
+ result, jsize(0), length, reinterpret_cast<const JNIElemType*>(data));
+ if (jenv->ExceptionCheck()) {
+ if (!IsOOMException(jenv)) {
+ // This exception isn't excepted due not to OOM. This is unrecoverable
+ // error.
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ }
+#ifdef MOZ_CHECK_JNI
+ jenv->ExceptionDescribe();
+#endif
+ jenv->ExceptionClear();
+ jenv->DeleteLocalRef(result);
+ return Base::LocalRef::Adopt(jenv, nullptr);
+ }
+ return Base::LocalRef::Adopt(jenv, result);
+ }
+
+ size_t Length() const {
+ const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ ElementType GetElement(size_t index) const {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+
+ ElementType ret;
+ (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
+ Base::Instance(), jsize(index), 1,
+ reinterpret_cast<JNIElemType*>(&ret));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsTArray<ElementType> GetElements() const {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+
+ const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
+
+ nsTArray<ElementType> array(len);
+ array.SetLength(len);
+ CopyTo(array.Elements(), len);
+ return array;
+ }
+
+ // returns number of elements copied
+ size_t CopyTo(ElementType* buffer, size_t size) const {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+
+ const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
+ const size_t amountToCopy = (len > size ? size : len);
+ (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
+ Base::Instance(), 0, jsize(amountToCopy),
+ reinterpret_cast<JNIElemType*>(buffer));
+ return amountToCopy;
+ }
+
+ ElementType operator[](size_t index) const { return GetElement(index); }
+
+ operator nsTArray<ElementType>() const { return GetElements(); }
+};
+
+#define DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
+ template <> \
+ class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> { \
+ public: \
+ explicit TypedObject(const Context& ctx) \
+ : ArrayRefBase<JNIType, ElementType>(ctx) {} \
+ static typename Base::LocalRef From(const nsTArray<ElementType>& aArray) { \
+ return New(aArray.Elements(), aArray.Length()); \
+ }
+
+#define DEFINE_PRIMITIVE_ARRAY_REF_FOOTER }
+
+#define DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \
+ ConvertFromType) \
+ static typename Base::LocalRef From( \
+ const nsTArray<ConvertFromType>& aArray) { \
+ return New(reinterpret_cast<const ElementType*>(aArray.Elements()), \
+ aArray.Length()); \
+ }
+
+#define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType) \
+ DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
+ DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
+
+#define DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION( \
+ JNIType, ElementType, ConvertFromType) \
+ DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
+ DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \
+ ConvertFromType) \
+ DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
+
+DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool);
+DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jbyteArray, int8_t,
+ uint8_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t);
+DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jshortArray, int16_t,
+ uint16_t);
+DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jintArray, int32_t,
+ uint32_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float);
+DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jlongArray, int64_t,
+ uint64_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double);
+
+#undef DEFINE_PRIMITIVE_ARRAY_REF
+#undef DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION
+#undef DEFINE_PRIMITIVE_ARRAY_HEADER
+#undef DEFINE_PRIMITIVE_ARRAY_FROM_IMPLICIT_CONVERSION
+#undef DEFINE_PRIMITIVE_ARRAY_FOOTER
+
+class ByteBuffer : public ObjectBase<ByteBuffer, jobject> {
+ public:
+ explicit ByteBuffer(const Context& ctx)
+ : ObjectBase<ByteBuffer, jobject>(ctx) {}
+
+ static LocalRef New(void* data, size_t capacity) {
+ JNIEnv* const env = GetEnvForThread();
+ const auto ret =
+ LocalRef::Adopt(env, env->NewDirectByteBuffer(data, jlong(capacity)));
+ MOZ_CATCH_JNI_EXCEPTION(env);
+ return ret;
+ }
+
+ void* Address() {
+ void* const ret = Env()->GetDirectBufferAddress(Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Env());
+ return ret;
+ }
+
+ size_t Capacity() {
+ const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance()));
+ MOZ_CATCH_JNI_EXCEPTION(Env());
+ return ret;
+ }
+};
+
+template <>
+const char ObjectBase<ByteBuffer, jobject>::name[];
+
+template <>
+class TypedObject<jobjectArray>
+ : public ObjectBase<TypedObject<jobjectArray>, jobjectArray> {
+ using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>;
+
+ public:
+ template <class Cls = Object>
+ static Base::LocalRef New(size_t length,
+ typename Cls::Param initialElement = nullptr) {
+ JNIEnv* const env = GetEnvForThread();
+ jobjectArray array = env->NewObjectArray(
+ jsize(length), typename Cls::Context(env, nullptr).ClassRef(),
+ initialElement.Get());
+ MOZ_CATCH_JNI_EXCEPTION(env);
+ return Base::LocalRef::Adopt(env, array);
+ }
+
+ explicit TypedObject(const Context& ctx) : Base(ctx) {}
+
+ size_t Length() const {
+ const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ Object::LocalRef GetElement(size_t index) const {
+ auto ret = Object::LocalRef::Adopt(
+ Base::Env(),
+ Base::Env()->GetObjectArrayElement(Base::Instance(), jsize(index)));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsTArray<Object::LocalRef> GetElements() const {
+ const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
+
+ nsTArray<Object::LocalRef> array((size_t(len)));
+ for (jsize i = 0; i < len; i++) {
+ array.AppendElement(Object::LocalRef::Adopt(
+ Base::Env(),
+ Base::Env()->GetObjectArrayElement(Base::Instance(), i)));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ }
+ return array;
+ }
+
+ Object::LocalRef operator[](size_t index) const { return GetElement(index); }
+
+ operator nsTArray<Object::LocalRef>() const { return GetElements(); }
+
+ void SetElement(size_t index, Object::Param element) const {
+ Base::Env()->SetObjectArrayElement(Base::Instance(), jsize(index),
+ element.Get());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ }
+};
+
+// Support conversion from LocalRef<T>* to LocalRef<Object>*:
+// LocalRef<Foo> foo;
+// Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*.
+// Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
+template <class Cls>
+class ReturnToLocal {
+ private:
+ LocalRef<Cls>* const localRef;
+ LocalRef<Object> objRef;
+
+ public:
+ explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {}
+ operator LocalRef<Object>*() { return &objRef; }
+
+ ~ReturnToLocal() {
+ if (objRef) {
+ *localRef = std::move(objRef);
+ }
+ }
+};
+
+template <class Cls>
+ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref) {
+ return ReturnToLocal<Cls>(ref);
+}
+
+// Support conversion from GlobalRef<T>* to LocalRef<Object/T>*:
+// GlobalRef<Foo> foo;
+// Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*.
+// Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
+template <class Cls>
+class ReturnToGlobal {
+ private:
+ GlobalRef<Cls>* const globalRef;
+ LocalRef<Object> objRef;
+ LocalRef<Cls> clsRef;
+
+ public:
+ explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {}
+ operator LocalRef<Object>*() { return &objRef; }
+ operator LocalRef<Cls>*() { return &clsRef; }
+
+ ~ReturnToGlobal() {
+ if (objRef) {
+ *globalRef = (clsRef = std::move(objRef));
+ } else if (clsRef) {
+ *globalRef = clsRef;
+ }
+ }
+};
+
+template <class Cls>
+ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref) {
+ return ReturnToGlobal<Cls>(ref);
+}
+
+// Make a LocalRef<T> from any other Ref<T>
+template <typename Cls, typename JNIType>
+LocalRef<Cls> ToLocalRef(const Ref<Cls, JNIType>& aRef) {
+ return LocalRef<Cls>(aRef);
+}
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Refs_h__