Files
yoga/lib/fb/include/fb/fbjni/CoreClasses-inl.h
2016-08-31 16:15:15 +01:00

664 lines
19 KiB
C++

/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <string.h>
#include <type_traits>
#include <stdlib.h>
#include "Common.h"
#include "Exceptions.h"
#include "Meta.h"
#include "MetaConvert.h"
namespace facebook {
namespace jni {
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
}
inline local_ref<JClass> JObject::getClass() const noexcept {
return adopt_local(internal::getEnv()->GetObjectClass(self()));
}
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
}
template<typename T>
inline T JObject::getFieldValue(JField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
field.set(self(), value);
}
inline std::string JObject::toString() const {
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
return method(self())->toStdString();
}
// Class is here instead of CoreClasses.h because we need
// alias_ref to be complete.
class MonitorLock {
public:
inline MonitorLock() noexcept;
inline MonitorLock(alias_ref<JObject> object) noexcept;
inline ~MonitorLock() noexcept;
inline MonitorLock(MonitorLock&& other) noexcept;
inline MonitorLock& operator=(MonitorLock&& other) noexcept;
inline MonitorLock(const MonitorLock&) = delete;
inline MonitorLock& operator=(const MonitorLock&) = delete;
private:
inline void reset() noexcept;
alias_ref<JObject> owned_;
};
MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
: owned_(object) {
internal::getEnv()->MonitorEnter(object.get());
}
void MonitorLock::reset() noexcept {
if (owned_) {
internal::getEnv()->MonitorExit(owned_.get());
if (internal::getEnv()->ExceptionCheck()) {
abort(); // Lock mismatch
}
owned_ = nullptr;
}
}
MonitorLock::~MonitorLock() noexcept {
reset();
}
MonitorLock::MonitorLock(MonitorLock&& other) noexcept
: owned_(other.owned_)
{
other.owned_ = nullptr;
}
MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
reset();
owned_ = other.owned_;
other.owned_ = nullptr;
return *this;
}
inline MonitorLock JObject::lock() const noexcept {
return MonitorLock(this_);
}
inline jobject JObject::self() const noexcept {
return this_;
}
inline void swap(JObject& a, JObject& b) noexcept {
using std::swap;
swap(a.this_, b.this_);
}
// JavaClass ///////////////////////////////////////////////////////////////////////////////////////
namespace detail {
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args) {
static auto cls = JC::javaClassStatic();
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
return cls->newObject(constructor, args...);
}
}
template <typename T, typename B, typename J>
auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
return static_cast<javaobject>(JObject::self());
}
// jclass //////////////////////////////////////////////////////////////////////////////////////////
namespace detail {
// This is not a real type. It is used so people won't accidentally
// use a void* to initialize a NativeMethod.
struct NativeMethodWrapper;
}
struct NativeMethod {
const char* name;
std::string descriptor;
detail::NativeMethodWrapper* wrapper;
};
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
return adopt_local(internal::getEnv()->GetSuperclass(self()));
}
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
const auto env = internal::getEnv();
JNINativeMethod jnimethods[methods.size()];
size_t i = 0;
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
jnimethods[i].name = it->name;
jnimethods[i].signature = it->descriptor.c_str();
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
}
auto result = env->RegisterNatives(self(), jnimethods, methods.size());
FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
}
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
const auto env = internal::getEnv();
const auto result = env->IsAssignableFrom(self(), other.get());
return result;
}
template<typename F>
inline JConstructor<F> JClass::getConstructor() const {
return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());
}
template<typename F>
inline JConstructor<F> JClass::getConstructor(const char* descriptor) const {
constexpr auto constructor_method_name = "<init>";
return getMethod<F>(constructor_method_name, descriptor);
}
template<typename F>
inline JMethod<F> JClass::getMethod(const char* name) const {
return getMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JMethod<F> JClass::getMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JMethod<F>{method};
}
template<typename F>
inline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {
return getStaticMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JStaticMethod<F> JClass::getStaticMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
const auto method = env->GetStaticMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JStaticMethod<F>{method};
}
template<typename F>
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(const char* name) const {
return getNonvirtualMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JNonvirtualMethod<F>{method};
}
template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>>
JClass::getField(const char* name) const {
return getField<T>(name, jtype_traits<T>::descriptor().c_str());
}
template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
auto field = env->GetFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JField<T>{field};
}
template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name) const {
return getStaticField<T>(name, jtype_traits<T>::descriptor().c_str());
}
template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
auto field = env->GetStaticFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JStaticField<T>{field};
}
template<typename T>
inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
field.set(self(), value);
}
template<typename R, typename... Args>
inline local_ref<R> JClass::newObject(
JConstructor<R(Args...)> constructor,
Args... args) const {
const auto env = internal::getEnv();
auto object = env->NewObject(self(), constructor.getId(),
detail::callToJni(
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
return adopt_local(static_cast<R>(object));
}
inline jclass JClass::self() const noexcept {
return static_cast<jclass>(JObject::self());
}
inline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {
findClassLocal(name)->registerNatives(methods);
}
// jstring /////////////////////////////////////////////////////////////////////////////////////////
inline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {
return make_jstring(modifiedUtf8.c_str());
}
namespace detail {
// convert to std::string from jstring
template <>
struct Convert<std::string> {
typedef jstring jniType;
static std::string fromJni(jniType t) {
return wrap_alias(t)->toStdString();
}
static jniType toJniRet(const std::string& t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const std::string& t) {
return make_jstring(t);
}
};
// convert return from const char*
template <>
struct Convert<const char*> {
typedef jstring jniType;
// no automatic synthesis of const char*. (It can't be freed.)
static jniType toJniRet(const char* t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const char* t) {
return make_jstring(t);
}
};
}
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
inline local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
static auto meth = javaClassStatic()->getMethod<javaobject(javaobject)>("initCause");
return meth(self(), cause.get());
}
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
namespace detail {
inline size_t JArray::size() const noexcept {
const auto env = internal::getEnv();
return env->GetArrayLength(self());
}
}
namespace detail {
template<typename Target>
inline ElementProxy<Target>::ElementProxy(
Target* target,
size_t idx)
: target_{target}, idx_{idx} {}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
target_->setElement(idx_, o);
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const ElementProxy<Target>& o) {
auto src = o.target_->getElement(o.idx_);
target_->setElement(idx_, src.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {
return target_->getElement(idx_);
}
template<typename Target>
inline ElementProxy<Target>::ElementProxy::operator local_ref<T> () {
return target_->getElement(idx_);
}
}
template <typename T>
std::string JArrayClass<T>::get_instantiated_java_descriptor() {
return "[" + jtype_traits<T>::descriptor();
};
template <typename T>
std::string JArrayClass<T>::get_instantiated_base_name() {
return get_instantiated_java_descriptor();
};
template<typename T>
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
const auto env = internal::getEnv();
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
return adopt_local(static_cast<javaobject>(rawArray));
}
template<typename T>
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
const auto env = internal::getEnv();
env->SetObjectArrayElement(this->self(), idx, value);
}
template<typename T>
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
const auto env = internal::getEnv();
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
return adopt_local(static_cast<T>(rawElement));
}
template<typename T>
inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {
return detail::ElementProxy<JArrayClass<T>>(this, index);
}
// jarray /////////////////////////////////////////////////////////////////////////////////////////
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
-> std::unique_ptr<T[]> {
using T = typename jtype_traits<JArrayType>::entry_type;
auto buf = std::unique_ptr<T[]>{new T[length]};
getRegion(start, length, buf.get());
return buf;
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
return jtype_traits<JArrayType>::descriptor();
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
return JPrimitiveArray::get_instantiated_java_descriptor();
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinCritical()
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
}
template <typename T>
class PinnedArrayAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
(void) start;
(void) length;
*elements = array->getElements(isCopy);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
(void) start;
(void) size;
array->releaseElements(elements, mode);
}
};
template <typename T>
class PinnedCriticalAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
const auto env = internal::getEnv();
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
const auto env = internal::getEnv();
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
}
};
template <typename T>
class PinnedRegionAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
auto buf = array->getRegion(start, length);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
*elements = buf.release();
*size = length;
*isCopy = true;
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
std::unique_ptr<T[]> holder;
if (mode == 0 || mode == JNI_ABORT) {
holder.reset(elements);
}
if (mode == 0 || mode == JNI_COMMIT) {
array->setRegion(start, size, elements);
}
}
};
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
template<typename T, typename Alloc>
PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
*this = std::move(o);
}
template<typename T, typename Alloc>
PinnedPrimitiveArray<T, Alloc>&
PinnedPrimitiveArray<T, Alloc>::operator=(PinnedPrimitiveArray&& o) {
if (array_) {
release();
}
array_ = std::move(o.array_);
elements_ = o.elements_;
isCopy_ = o.isCopy_;
size_ = o.size_;
start_ = o.start_;
o.clear();
return *this;
}
template<typename T, typename Alloc>
T* PinnedPrimitiveArray<T, Alloc>::get() {
return elements_;
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::release() {
releaseImpl(0);
clear();
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::commit() {
releaseImpl(JNI_COMMIT);
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::abort() {
releaseImpl(JNI_ABORT);
clear();
}
template <typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);
Alloc::release(array_, elements_, start_, size_, mode);
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {
array_ = nullptr;
elements_ = nullptr;
isCopy_ = false;
start_ = 0;
size_ = 0;
}
template<typename T, typename Alloc>
inline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {
FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
return elements_[index];
}
template<typename T, typename Alloc>
inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
return isCopy_ == JNI_TRUE;
}
template<typename T, typename Alloc>
inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
return size_;
}
template<typename T, typename Alloc>
inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
if (elements_) {
release();
}
}
template<typename T, typename Alloc>
inline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(alias_ref<typename jtype_traits<T>::array_type> array, jint start, jint length) {
array_ = array;
start_ = start;
Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);
}
template<typename T, typename Base, typename JType>
inline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {
static auto cls = findClassStatic(jtype_traits<typename T::javaobject>::base_name().c_str());
return cls;
}
template<typename T, typename Base, typename JType>
inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());
return findClassLocal(className.c_str());
}
}}