Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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__