cxx/fbjni/detail/References.h (303 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @file References.h
*
* Functionality similar to smart pointers, but for references into the VM. Four main reference
* types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic
* templates that and refer to objects in the jobject hierarchy. The type of the referred objects
* are specified using the template parameter. All reference types except alias_ref own their
* underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context
* of std smart pointers, these references behave like unique_ptr, and have basically the same
* interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying
* JNI reference (like the parameters passed directly to JNI functions), is released. The alias
* references provides no ownership and is a simple wrapper for plain JNI references.
*
* All but the weak references provides access to the underlying object using dereferencing, and a
* get() method. It is also possible to convert these references to booleans to test for nullity.
* To access the underlying object of a weak reference, the reference must either be released, or
* the weak reference can be used to create a local or global reference.
*
* An owning reference is created either by moving the reference from an existing owned reference,
* by copying an existing owned reference (which creates a new underlying reference), by using the
* default constructor which initialize the reference to nullptr, or by using a helper function. The
* helper function exist in two flavors: make_XXX or adopt_XXX.
*
* Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the
* plain JNI reference so be sure that no one else owns the reference when you adopt it, and make
* sure that you know what kind of reference it is.
*
* New owned references can be created from existing plain JNI references, alias references, local
* references, and global references (i.e. non-weak references) using the make_local, make_global,
* and make_weak functions.
*
* Alias references can be implicitly initialized using global, local and plain JNI references using
* the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather
* create a separate reference that we do own, leaving the passed-in reference to its fate.
*
* Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart
* reference of the same type. In the case of copy assignment a new reference is created. Alias
* reference can also be assigned new values, but since they are simple wrappers of plain JNI
* references there is no move semantics involved.
*
* Alias references are special in that they do not own the object and can therefore safely be
* converted to and from its corresponding plain JNI reference. They are useful as parameters of
* functions that do not affect the lifetime of a reference. Usage can be compared with using plain
* JNI pointers as parameters where a function does not take ownership of the underlying object.
*
* The local, global, and alias references makes it possible to access methods in the underlying
* objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are
* supported (see example below). The wrappers also supports inheritance so a wrapper can inherit
* from another wrapper to gain access to its functionality. As an example the jstring wrapper
* inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for
* example call the toString() method using the jclass wrapper, or any other class that inherits
* from the jobject wrapper.
*
* Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if
* you have a jobject that refers to a Java String you will need to cast it to jstring to get the
* jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one
* stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast
* wisely.
*
* @include WrapperSample.cpp
*/
#pragma once
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <jni.h>
#include "ReferenceAllocators.h"
#include "TypeTraits.h"
#include "References-forward.h"
namespace facebook {
namespace jni {
/// Convenience function to wrap an existing local reference
template<typename T>
local_ref<T> adopt_local(T ref) noexcept;
/// Convenience function to wrap an existing global reference
template<typename T>
global_ref<T> adopt_global(T ref) noexcept;
/// Convenience function to wrap an existing weak reference
template<typename T>
weak_ref<T> adopt_weak_global(T ref) noexcept;
/// Swaps two owning references of the same type
template<typename T>
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
/// Swaps two owning references of the same type
template<typename T, typename Alloc>
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
/**
* Retrieve the plain reference from a plain reference.
*/
template<typename T>
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
/**
* Retrieve the plain reference from an alias reference.
*/
template<typename T>
JniType<T> getPlainJniReference(alias_ref<T> ref);
/**
* Retrieve the plain JNI reference from any reference owned reference.
*/
template<typename T, typename Alloc>
JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
class JObject;
class JClass;
namespace detail {
template<typename T>
constexpr bool IsJavaClassType() {
return std::is_base_of<JObject, T>::value;
}
template <typename T, typename Enable = void>
struct HasJniRefRepr : std::false_type {};
template <typename T>
struct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {
using type = typename T::JniRefRepr;
};
template <typename T>
struct RefReprType<T*> {
static_assert(HasJniRefRepr<T>::value, "Repr type missing JniRefRepr.");
using type = typename HasJniRefRepr<T>::type;
static_assert(IsJavaClassType<type>(),
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct RefReprType<T, typename std::enable_if<IsJavaClassType<T>(), void>::type> {
using type = T;
static_assert(IsJavaClassType<type>(),
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct JavaObjectType {
using type = typename RefReprType<T>::type::javaobject;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct JavaObjectType<T*> {
using type = T*;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct PrimitiveOrJavaObjectType<T, enable_if_t<IsJniPrimitive<T>(), void>> {
using type = T;
static_assert(IsJniPrimitive<type>(),
"PrimitiveOrJavaObjectType<T> not a jni primitive");
static_assert(std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
"PrimitiveOrJavaObjectType<T> not idempotent");
};
template <typename T>
struct PrimitiveOrJavaObjectType<T, enable_if_t<IsPlainJniReference<T>(), void>> {
using type = T;
static_assert(IsPlainJniReference<type>(),
"PrimitiveOrJavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
"PrimitiveOrJavaObjectType<T> not idempotent");
};
template <typename T>
struct PrimitiveOrJavaObjectType<T, enable_if_t<IsJavaClassType<T>(), void>> {
using type = JniType<T>;
static_assert(IsPlainJniReference<type>(),
"PrimitiveOrJavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
"PrimitiveOrJavaObjectType<T> not idempotent");
};
template <typename Repr>
struct ReprStorage {
explicit ReprStorage(JniType<Repr> obj) noexcept;
void set(JniType<Repr> obj) noexcept;
Repr& get() noexcept;
const Repr& get() const noexcept;
JniType<Repr> jobj() const noexcept;
void swap(ReprStorage& other) noexcept;
ReprStorage() = delete;
ReprStorage(const ReprStorage&) = delete;
ReprStorage(ReprStorage&&) = delete;
ReprStorage& operator=(const ReprStorage&) = delete;
ReprStorage& operator=(ReprStorage&&) = delete;
private:
using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
Storage storage_;
};
} // namespace detail
/**
* Create a new local reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned local reference (referring to null if the input does)
* @throws std::bad_alloc if the JNI reference could not be created
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T& r);
/**
* Create a new global reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned global reference (referring to null if the input does)
* @throws std::bad_alloc if the JNI reference could not be created
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
make_global(const T& r);
/**
* Create a new weak global reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned weak global reference (referring to null if the input does)
* @throws std::bad_alloc if the returned reference is null
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T& r);
/**
* Compare two references to see if they refer to the same object
*/
template<typename T1, typename T2>
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator==(const T1& a, const T2& b);
/**
* Compare two references to see if they don't refer to the same object
*/
template<typename T1, typename T2>
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator!=(const T1& a, const T2& b);
/**
* Compare references against nullptr
*/
template<typename T1>
enable_if_t<IsNonWeakReference<T1>(), bool>
operator==(const T1& a, std::nullptr_t);
template<typename T1>
enable_if_t<IsNonWeakReference<T1>(), bool>
operator==(std::nullptr_t, const T1& a);
template<typename T1>
enable_if_t<IsNonWeakReference<T1>(), bool>
operator!=(const T1& a, std::nullptr_t);
template<typename T1>
enable_if_t<IsNonWeakReference<T1>(), bool>
operator!=(std::nullptr_t, const T1& a);
template<typename T, typename Alloc>
class base_owned_ref {
public:
using javaobject = JniType<T>;
/**
* Release the ownership and set the reference to null. Thus no deleter is invoked.
* @return Returns the reference
*/
javaobject release() noexcept;
/**
* Reset the reference to refer to nullptr.
*/
void reset() noexcept;
protected:
using Repr = ReprType<T>;
detail::ReprStorage<Repr> storage_;
javaobject get() const noexcept;
void set(javaobject ref) noexcept;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit base_owned_ref(javaobject reference) noexcept;
/// Create a null reference
base_owned_ref() noexcept;
/// Create a null reference
explicit base_owned_ref(std::nullptr_t) noexcept;
/// Copy constructor (note creates a new reference)
base_owned_ref(const base_owned_ref& other);
template<typename U>
base_owned_ref(const base_owned_ref<U, Alloc>& other);
/// Transfers ownership of an underlying reference from one unique reference to another
base_owned_ref(base_owned_ref&& other) noexcept;
template<typename U>
base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;
/// The delete the underlying reference if applicable
~base_owned_ref() noexcept;
/// Assignment operator (note creates a new reference)
base_owned_ref& operator=(const base_owned_ref& other);
/// Assignment by moving a reference thus not creating a new reference
base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
void reset(javaobject reference) noexcept;
friend javaobject jni::getPlainJniReference<>(const base_owned_ref<T, Alloc>& ref);
template<typename U, typename UAlloc>
friend class base_owned_ref;
};
/**
* A smart reference that owns its underlying JNI reference. The class provides basic
* functionality to handle a reference but gives no access to it unless the reference is
* released, thus no longer owned. The API is stolen with pride from unique_ptr and the
* semantics should be basically the same. This class should not be used directly, instead use
* @ref weak_ref
*/
template<typename T>
class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
public:
using javaobject = JniType<T>;
using Allocator = WeakGlobalReferenceAllocator;
// This inherits non-default, non-copy, non-move ctors.
using base_owned_ref<T, Allocator>::base_owned_ref;
/// Create a null reference
weak_ref() noexcept
: base_owned_ref<T, Allocator>{} {}
/// Create a null reference
/* implicit */ weak_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Allocator>{nullptr} {}
/// Copy constructor (note creates a new reference)
weak_ref(const weak_ref& other)
: base_owned_ref<T, Allocator>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
weak_ref(const weak_ref<U>& other)
: base_owned_ref<T, Allocator>{other} {}
/// Transfers ownership of an underlying reference from one unique reference to another
weak_ref(weak_ref&& other) noexcept
: base_owned_ref<T, Allocator>{std::move(other)} {}
// Move from ref to compatible type.
template<typename U>
weak_ref(weak_ref<U>&& other)
: base_owned_ref<T, Allocator>{std::move(other)} {}
/// Assignment operator (note creates a new reference)
weak_ref& operator=(const weak_ref& other);
/// Assignment by moving a reference thus not creating a new reference
weak_ref& operator=(weak_ref&& rhs) noexcept;
// Creates an owned local reference to the referred object or to null if the object is reclaimed
local_ref<T> lockLocal() const;
// Creates an owned global reference to the referred object or to null if the object is reclaimed
global_ref<T> lockGlobal() const;
private:
// get/release/reset on weak_ref are not exposed to users.
using base_owned_ref<T, Allocator>::get;
using base_owned_ref<T, Allocator>::release;
using base_owned_ref<T, Allocator>::reset;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit weak_ref(javaobject reference) noexcept
: base_owned_ref<T, Allocator>{reference} {}
template<typename T2> friend class weak_ref;
friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;
friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
};
/**
* A class representing owned strong references to Java objects. This class
* should not be used directly, instead use @ref local_ref, or @ref global_ref.
*/
template<typename T, typename Alloc>
class basic_strong_ref : public base_owned_ref<T, Alloc> {
using typename base_owned_ref<T, Alloc>::Repr;
public:
using javaobject = JniType<T>;
using Allocator = Alloc;
// This inherits non-default, non-copy, non-move ctors.
using base_owned_ref<T, Alloc>::base_owned_ref;
using base_owned_ref<T, Alloc>::release;
using base_owned_ref<T, Alloc>::reset;
/// Create a null reference
basic_strong_ref() noexcept
: base_owned_ref<T, Alloc>{} {}
/// Create a null reference
/* implicit */ basic_strong_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Alloc>{nullptr} {}
/// Copy constructor (note creates a new reference)
basic_strong_ref(const basic_strong_ref& other)
: base_owned_ref<T, Alloc>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
: base_owned_ref<T, Alloc>{other} {}
// Move from ref to compatible type.
template<typename U>
basic_strong_ref(basic_strong_ref<U, Alloc>&& other)
: base_owned_ref<T, Alloc>{std::move(other)} {}
/// Transfers ownership of an underlying reference from one unique reference to another
basic_strong_ref(basic_strong_ref&& other) noexcept
: base_owned_ref<T, Alloc>{std::move(other)} {}
/// Assignment operator (note creates a new reference)
basic_strong_ref& operator=(const basic_strong_ref& other);
/// Assignment by moving a reference thus not creating a new reference
basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
/// Get the plain JNI reference
using base_owned_ref<T, Allocator>::get;
/// Release the ownership of the reference and return the wrapped reference in an alias
alias_ref<T> releaseAlias() noexcept;
/// Checks if the reference points to a non-null object
explicit operator bool() const noexcept;
/// Access the functionality provided by the object wrappers
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const Repr* operator->() const noexcept;
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
Repr& operator*() noexcept;
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
/// before invoking)
const Repr& operator*() const noexcept;
private:
using base_owned_ref<T, Alloc>::storage_;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit basic_strong_ref(javaobject reference) noexcept
: base_owned_ref<T, Alloc>{reference} {}
friend local_ref<T> adopt_local<T>(T ref) noexcept;
friend global_ref<T> adopt_global<T>(T ref) noexcept;
friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
};
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
/// Swaps to alias reference of the same type
template<typename T>
void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
/**
* A non-owning variant of the smart references (a dumb
* reference). Use this representation when you don't want to claim
* ownership of the underlying reference (compare to using raw
* pointers instead of smart pointers.)
*/
template<typename T>
class alias_ref {
using Repr = ReprType<T>;
public:
using javaobject = JniType<T>;
/// Create a null reference
alias_ref() noexcept;
/// Create a null reference
/* implicit */ alias_ref(std::nullptr_t) noexcept;
/// Copy constructor
alias_ref(const alias_ref& other) noexcept;
/// Wrap an existing plain JNI reference
/* implicit */ alias_ref(javaobject ref) noexcept;
/// Wrap an existing smart reference of any type convertible to T
template<
typename TOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(alias_ref<TOther> other) noexcept;
/// Wrap an existing alias reference of a type convertible to T
template<
typename TOther,
typename AOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
/// Assignment operator
alias_ref& operator=(alias_ref other) noexcept;
/// Checks if the reference points to a non-null object
explicit operator bool() const noexcept;
/// Converts back to a plain JNI reference
javaobject get() const noexcept;
/// Access the functionality provided by the object wrappers
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const Repr* operator->() const noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
Repr& operator*() noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
const Repr& operator*() const noexcept;
private:
void set(javaobject ref) noexcept;
detail::ReprStorage<Repr> storage_;
friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
};
/**
* RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.
*
* This is useful when you have a call which is initiated from C++-land, and therefore
* doesn't automatically get a local JNI frame managed for you by the JNI framework.
*/
class JniLocalScope {
public:
JniLocalScope(JNIEnv* p_env, jint capacity);
~JniLocalScope();
private:
JNIEnv* env_;
bool hasFrame_;
};
template<typename T, typename U>
enable_if_t<IsPlainJniReference<JniType<T>>(), local_ref<T>>
static_ref_cast(const local_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<JniType<T>>(), global_ref<T>>
static_ref_cast(const global_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<JniType<T>>(), alias_ref<T>>
static_ref_cast(const alias_ref<U>& ref) noexcept;
template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<JniType<T>>(), decltype(static_ref_cast<T>(ref))> ;
}}
#include "References-inl.h"