#pragma once namespace jni { /* Ownership type and main interface for high-level references. Client code using the high-level API will most often work with values of this class template, using the following aliases: * Global

, a.k.a. Unique>, * Weak

, a.k.a. Unique>, * Local

, a.k.a. Unique, where P is Object<>, Class<>, or Array<>. `Unique` is an ownership class with a deletion policy that's parameterized both by the appropriate method to delete the reference (global, weak, or local) and (for global and weak references), a choice about how to obtain the JNIEnv that's necessary to call the deletion method. The default policy is to delete the reference using the same JNIEnv as was passed to the constructor, but in cases where the object may be deleted on a different thread (commonly, the Java finalizer thread), EnvGettingDeleter or EnvAttachingDeleter may be needed. Object<>, Class<>, or Array<> -- the underlying and inherited types used for the template parameter T -- are not publicly constructible or destructible. This is to ensure that code works only with ownership types which release the reference at an appropriate time. Our experience has shown that this is important even for local references; the default JVM cleanup behaviors for local references are not enough to ensure that the local reference table never overflows. You may also pass or receive C++ references when you wish to borrow rather than take ownership. For example, receiving parameters for a native method implementation should be C++ references, reflecting the fact that JVM implementations prohibit explicitly releasing this form of local reference. However, if you need to store or copy the reference, you will need to use a method such as `NewGlobalRef` that copies at the reference level. `Unique`, `Object<>`, etc., are not themselves copyable. */ template < class T, class D > class Unique : public T { private: D deleter; Unique(const Unique&) = delete; Unique& operator=(const Unique&) = delete; public: using Base = T; using UntaggedType = typename T::UntaggedType; explicit Unique(std::nullptr_t ptr = nullptr) : T(ptr), deleter() {} explicit Unique(JNIEnv& env, UntaggedType* ptr) : T(ptr), deleter(env) {} Unique(Unique&& other) : T(other.release()), deleter(std::move(other.get_deleter())) {} template < class U > Unique(Unique&& other, std::enable_if_t::value>* = nullptr) : T(other.release()), deleter(std::move(other.get_deleter())) {} ~Unique() { reset(); } Unique& operator=(Unique&& other) { reset(other.release()); deleter = std::move(other.deleter); return *this; } void reset(UntaggedType* ptr = nullptr) { UntaggedType* current = this->get(); T::reset(ptr); if (current) { get_deleter()(current); } } UntaggedType* release() { UntaggedType* current = this->get(); T::reset(nullptr); return current; } D& get_deleter() { return deleter; } const D& get_deleter() const { return deleter; } }; template < class T, template < RefDeletionMethod > class Deleter = DefaultRefDeleter > using Global = Unique< T, Deleter<&JNIEnv::DeleteGlobalRef> >; template < class T, template < RefDeletionMethod > class Deleter = DefaultRefDeleter > using Weak = Unique< T, Deleter<&JNIEnv::DeleteWeakGlobalRef> >; // Not parameterized by Deleter because local references should be short-lived enough // that DefaultRefDeleter suffices in all cases. template < class T > using Local = Unique< T, DefaultRefDeleter<&JNIEnv::DeleteLocalRef> >; // Special case for JNI-provided input parameters to native methods, which apparently // should not be explicitly deleted (https://bugs.chromium.org/p/chromium/issues/detail?id=506850). struct NullDeleter { NullDeleter() = default; NullDeleter(JNIEnv&) {} void operator()(jobject*) const {} }; template < class T > using Input = Unique< T, NullDeleter >; template < class T > struct RemoveUnique { using Type = T; }; template < class T, class D > struct RemoveUnique< Unique > { using Type = T; }; template < class T > using RemoveUniqueType = typename RemoveUnique::Type; template < class T > auto ReleaseUnique(T primitive) { return primitive; } template < class T, class D > auto ReleaseUnique(Unique&& t) { return t.release(); } template < template < RefDeletionMethod > class Deleter, class T > auto NewGlobal(JNIEnv& env, const T& t) { return Global, Deleter>(env, reinterpret_cast(jni::NewGlobalRef(env, t.get()).release())); } template < class T > auto NewGlobal(JNIEnv& env, const T& t) { return NewGlobal(env, t); } // Attempt to promote a weak reference to a strong one. Returns an empty result // if the weak reference has expired. // // Beware that the semantics of JNI weak references are weaker than is typically // desired: a JNI weak reference may still be promoted to a non-null strong reference // even during finalization. Consider using jni::WeakReference instead. template < template < RefDeletionMethod > class Deleter, class T, template < RefDeletionMethod > class WeakDeleter > Global NewGlobal(JNIEnv& env, const Weak& t) { jobject* obj = Wrap(env.NewGlobalRef(Unwrap(t->get()))); CheckJavaException(env); return Global(env, obj); } template < class T > Global NewGlobal(JNIEnv& env, const Weak& t) { return NewGlobal(env, t); } template < template < RefDeletionMethod > class Deleter, class T > auto NewWeak(JNIEnv& env, const T& t) { return Weak, Deleter>(env, reinterpret_cast(jni::NewWeakGlobalRef(env, t.get()).release())); } template < class T > auto NewWeak(JNIEnv& env, const T& t) { return NewWeak(env, t); } template < class T > auto NewLocal(JNIEnv& env, const T& t) { return Local>(env, reinterpret_cast(jni::NewLocalRef(env, t.get()).release())); } // Attempt to promote a weak reference to a strong one. Returns an empty result // if the weak reference has expired. // // Beware that the semantics of JNI weak references are weaker than is typically // desired: a JNI weak reference may still be promoted to a non-null strong reference // even during finalization. Consider using jni::WeakReference instead. template < class T, template < RefDeletionMethod > class WeakDeleter > Local NewLocal(JNIEnv& env, const Weak& t) { jobject* obj = Wrap(env.NewLocalRef(Unwrap(t->get()))); CheckJavaException(env); return Local(env, obj); } }