diff options
Diffstat (limited to 'widget/android/jni/Refs.h')
-rw-r--r-- | widget/android/jni/Refs.h | 1135 |
1 files changed, 1135 insertions, 0 deletions
diff --git a/widget/android/jni/Refs.h b/widget/android/jni/Refs.h new file mode 100644 index 0000000000..29812fe6a5 --- /dev/null +++ b/widget/android/jni/Refs.h @@ -0,0 +1,1135 @@ +/* -*- 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; + } + + static LocalRef New(void* data, size_t capacity, const fallible_t&) { + JNIEnv* const env = GetEnvForThread(); + const jobject result = env->NewDirectByteBuffer(data, jlong(capacity)); + if (env->ExceptionCheck()) { + if (!IsOOMException(env)) { + // This exception isn't excepted due not to OOM. This is unrecoverable + // error. + MOZ_CATCH_JNI_EXCEPTION(env); + } +#ifdef MOZ_CHECK_JNI + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + return LocalRef::Adopt(env, nullptr); + } + return LocalRef::Adopt(env, result); + } + + 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__ |