/* -*- 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 #include #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 Ref; // Represents a calling context for JNI methods. template class Context; // Wrapped local reference that inherits from Ref. template class LocalRef; // Wrapped global reference that inherits from Ref. template class GlobalRef; // Wrapped weak reference that inherits from Ref. template class WeakRef; // Wrapped dangling reference that's owned by someone else. template 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 template struct Args {}; class Object; // Base class for Ref and its specializations. template class Ref { template friend class Ref; using Self = Ref; 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 { CopyableCtx(JNIEnv* env, Type instance) : Context(env, instance) {} CopyableCtx(const CopyableCtx& cls) : Context(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; 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 From(JNIType obj) { return Ref(obj); } // Construct a Ref form a generic object reference. static Ref From(const Ref& obj) { return Ref(JNIType(obj.Get())); } MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {} // Get the raw JNI reference. JNIType Get() const { return mInstance; } template bool IsInstanceOf() const { return FindEnv()->IsInstanceOf(mInstance, typename T::Context().ClassRef()); } template typename T::Ref Cast() const { #ifdef MOZ_CHECK_JNI MOZ_RELEASE_ASSERT(FindEnv()->IsAssignableFrom( Context().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() const { return Ref(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 Context : public Ref { using Ref = jni::Ref; 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 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& operator*() const { return *this; } }; template jclass Context::sClassRef; template class ObjectBase { protected: const jni::Context& mCtx; jclass ClassRef() const { return mCtx.ClassRef(); } JNIEnv* Env() const { return mCtx.Env(); } Type Instance() const { return mCtx.Get(); } public: using Ref = jni::Ref; using Context = jni::Context; using LocalRef = jni::LocalRef; using GlobalRef = jni::GlobalRef; using WeakRef = jni::WeakRef; 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(this); } }; // Binding for a plain jobject. class Object : public ObjectBase { public: explicit Object(const Context& ctx) : ObjectBase(ctx) {} }; // Binding for a built-in object reference other than jobject. template class TypedObject : public ObjectBase, T> { public: explicit TypedObject(const Context, T>& ctx) : ObjectBase, T>(ctx) {} }; // Binding for a boxed primitive object. template class BoxedObject : public ObjectBase, jobject> { public: explicit BoxedObject(const Context, jobject>& ctx) : ObjectBase, jobject>(ctx) {} }; template <> const char ObjectBase::name[]; template <> const char ObjectBase, jstring>::name[]; template <> const char ObjectBase, jclass>::name[]; template <> const char ObjectBase, jthrowable>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jobject>::name[]; template <> const char ObjectBase, jbooleanArray>::name[]; template <> const char ObjectBase, jbyteArray>::name[]; template <> const char ObjectBase, jcharArray>::name[]; template <> const char ObjectBase, jshortArray>::name[]; template <> const char ObjectBase, jintArray>::name[]; template <> const char ObjectBase, jlongArray>::name[]; template <> const char ObjectBase, jfloatArray>::name[]; template <> const char ObjectBase, jdoubleArray>::name[]; template <> const char ObjectBase, jobjectArray>::name[]; // Define bindings for built-in types. using String = TypedObject; using Class = TypedObject; using Throwable = TypedObject; using Boolean = BoxedObject; using Byte = BoxedObject; using Character = BoxedObject; using Short = BoxedObject; using Integer = BoxedObject; using Long = BoxedObject; using Float = BoxedObject; using Double = BoxedObject; using BooleanArray = TypedObject; using ByteArray = TypedObject; using CharArray = TypedObject; using ShortArray = TypedObject; using IntArray = TypedObject; using LongArray = TypedObject; using FloatArray = TypedObject; using DoubleArray = TypedObject; using ObjectArray = TypedObject; namespace detail { // See explanation in LocalRef. template struct GenericObject { using Type = Object; }; template <> struct GenericObject { struct Type { using Ref = jni::Ref; using Context = jni::Context; }; }; template struct GenericLocalRef { template struct Type : jni::Object {}; }; template <> struct GenericLocalRef { template using Type = jni::LocalRef; }; } // namespace detail template class LocalRef : public Cls::Context { template 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 to LocalRef, we // need constructors and copy assignment operators that take in a // LocalRef argument. However, if Cls *is* Object, we would have // duplicated constructors and operators with LocalRef arguments. To // avoid this conflict, we use GenericObject, which is defined as Object for // LocalRef and defined as a dummy class for LocalRef. using GenericObject = typename detail::GenericObject::Type; // Similarly, GenericLocalRef is useed to convert LocalRef to, // LocalRef. It's defined as LocalRef for Cls == Object, // and defined as a dummy template class for Cls != Object. template using GenericLocalRef = typename detail::GenericLocalRef::template Type; 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& ref) : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance)) {} // Move constructor. LocalRef(LocalRef&& 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 into a LocalRef without // creating/deleting local references. MOZ_IMPLICIT LocalRef(LocalRef&& ref) : Ctx(ref.mEnv, JNIType(ref.mInstance)) { ref.mInstance = nullptr; } template MOZ_IMPLICIT LocalRef(GenericLocalRef&& 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& operator=(LocalRef ref) & { return swap(ref); } LocalRef& operator=(const Ref& ref) & { LocalRef newRef(Ctx::mEnv, ref); return swap(newRef); } LocalRef& operator=(LocalRef&& ref) & { LocalRef newRef(std::move(ref)); return swap(newRef); } template LocalRef& operator=(GenericLocalRef&& ref) & { LocalRef newRef(std::move(ref)); return swap(newRef); } LocalRef& operator=(decltype(nullptr)) & { LocalRef newRef(Ctx::mEnv, nullptr); return swap(newRef); } }; template 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& 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& operator=(GlobalRef ref) & { return swap(ref); } GlobalRef& operator=(const Ref& ref) & { GlobalRef newRef(ref); return swap(newRef); } GlobalRef& operator=(const LocalRef& ref) & { GlobalRef newRef(ref); return swap(newRef); } GlobalRef& operator=(decltype(nullptr)) & { GlobalRef newRef(nullptr); return swap(newRef); } }; template class WeakRef : public Ref { using Ref = Ref; 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& 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& operator=(WeakRef ref) & { return swap(ref); } WeakRef& operator=(const Ref& ref) & { WeakRef newRef(ref); return swap(newRef); } WeakRef& operator=(const LocalRef& ref) & { WeakRef newRef(ref); return swap(newRef); } WeakRef& operator=(decltype(nullptr)) & { WeakRef newRef(nullptr); return swap(newRef); } void operator->() const = delete; void operator*() const = delete; }; template 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 : public ObjectBase, jstring> { using Base = ObjectBase, 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(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(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(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 struct TypeAdapter; } // Ref specialization for arrays. template class ArrayRefBase : public ObjectBase, JNIType> { protected: using Base = ObjectBase, JNIType>; public: explicit ArrayRefBase(const Context, JNIType>& ctx) : Base(ctx) {} static typename Base::LocalRef New(const ElementType* data, size_t length) { using JNIElemType = typename detail::TypeAdapter::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::NewArray)(length); MOZ_CATCH_JNI_EXCEPTION(jenv); (jenv->*detail::TypeAdapter::SetArray)( result, jsize(0), length, reinterpret_cast(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::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::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::SetArray)( result, jsize(0), length, reinterpret_cast(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::JNIType; static_assert(sizeof(ElementType) == sizeof(JNIElemType), "Size of native type must match size of JNI type"); ElementType ret; (Base::Env()->*detail::TypeAdapter::GetArray)( Base::Instance(), jsize(index), 1, reinterpret_cast(&ret)); MOZ_CATCH_JNI_EXCEPTION(Base::Env()); return ret; } nsTArray GetElements() const { using JNIElemType = typename detail::TypeAdapter::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 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::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::GetArray)( Base::Instance(), 0, jsize(amountToCopy), reinterpret_cast(buffer)); return amountToCopy; } ElementType operator[](size_t index) const { return GetElement(index); } operator nsTArray() const { return GetElements(); } }; #define DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \ template <> \ class TypedObject : public ArrayRefBase { \ public: \ explicit TypedObject(const Context& ctx) \ : ArrayRefBase(ctx) {} \ static typename Base::LocalRef From(const nsTArray& 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& aArray) { \ return New(reinterpret_cast(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 { public: explicit ByteBuffer(const Context& ctx) : ObjectBase(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::name[]; template <> class TypedObject : public ObjectBase, jobjectArray> { using Base = ObjectBase, jobjectArray>; public: template 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 GetElements() const { const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance())); nsTArray 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() 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* to LocalRef*: // LocalRef foo; // Foo::GetFoo(&foo); // error because parameter type is LocalRef*. // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument. template class ReturnToLocal { private: LocalRef* const localRef; LocalRef objRef; public: explicit ReturnToLocal(LocalRef* ref) : localRef(ref) {} operator LocalRef*() { return &objRef; } ~ReturnToLocal() { if (objRef) { *localRef = std::move(objRef); } } }; template ReturnToLocal ReturnTo(LocalRef* ref) { return ReturnToLocal(ref); } // Support conversion from GlobalRef* to LocalRef*: // GlobalRef foo; // Foo::GetFoo(&foo); // error because parameter type is LocalRef*. // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument. template class ReturnToGlobal { private: GlobalRef* const globalRef; LocalRef objRef; LocalRef clsRef; public: explicit ReturnToGlobal(GlobalRef* ref) : globalRef(ref) {} operator LocalRef*() { return &objRef; } operator LocalRef*() { return &clsRef; } ~ReturnToGlobal() { if (objRef) { *globalRef = (clsRef = std::move(objRef)); } else if (clsRef) { *globalRef = clsRef; } } }; template ReturnToGlobal ReturnTo(GlobalRef* ref) { return ReturnToGlobal(ref); } // Make a LocalRef from any other Ref template LocalRef ToLocalRef(const Ref& aRef) { return LocalRef(aRef); } } // namespace jni } // namespace mozilla #endif // mozilla_jni_Refs_h__