commit changes in fbjni
This commit is contained in:
@@ -9,7 +9,7 @@ android {
|
|||||||
targetSdkVersion rootProject.targetSdkVersion
|
targetSdkVersion rootProject.targetSdkVersion
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
abiFilters 'arm64-v8a', 'x86'
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
|
|||||||
@@ -5,281 +5,227 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#pragma once
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <memory>
|
#include <fb/assert.h>
|
||||||
#include <type_traits>
|
#include <fb/visibility.h>
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
#include "CoreClasses.h"
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class BaseHybridClass {
|
class BaseHybridClass {
|
||||||
public:
|
public:
|
||||||
virtual ~BaseHybridClass() {}
|
virtual ~BaseHybridClass() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HybridData : public JavaClass<HybridData> {
|
struct FBEXPORT HybridData : public JavaClass<HybridData> {
|
||||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
||||||
static local_ref<HybridData> create();
|
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
|
||||||
};
|
BaseHybridClass* getNativePointer();
|
||||||
|
static local_ref<HybridData> create();
|
||||||
|
};
|
||||||
|
|
||||||
class HybridDestructor : public JavaClass<HybridDestructor> {
|
template <typename Base, typename Enabled = void>
|
||||||
public:
|
struct HybridTraits {
|
||||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
|
// This static assert should actually always fail if we don't use one of the
|
||||||
|
// specializations below.
|
||||||
|
static_assert(
|
||||||
|
std::is_base_of<JObject, Base>::value ||
|
||||||
|
std::is_base_of<BaseHybridClass, Base>::value,
|
||||||
|
"The base of a HybridClass must be either another HybridClass or derived from JObject.");
|
||||||
|
};
|
||||||
|
|
||||||
detail::BaseHybridClass* getNativePointer();
|
template <>
|
||||||
|
struct HybridTraits<BaseHybridClass> {
|
||||||
|
using CxxBase = BaseHybridClass;
|
||||||
|
using JavaBase = JObject;
|
||||||
|
};
|
||||||
|
|
||||||
void setNativePointer(std::unique_ptr<detail::BaseHybridClass> new_value);
|
template <typename Base>
|
||||||
};
|
struct HybridTraits<
|
||||||
|
Base,
|
||||||
|
typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {
|
||||||
|
using CxxBase = Base;
|
||||||
|
using JavaBase = typename Base::JavaPart;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename Base>
|
||||||
detail::BaseHybridClass* getNativePointer(T t) {
|
struct HybridTraits<
|
||||||
return getHolder(t)->getNativePointer();
|
Base,
|
||||||
}
|
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
|
||||||
|
using CxxBase = BaseHybridClass;
|
||||||
|
using JavaBase = Base;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
// convert to HybridClass* from jhybridobject
|
||||||
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
|
template <typename T>
|
||||||
getHolder(t)->setNativePointer(std::move(new_value));
|
struct FBEXPORT Convert<
|
||||||
}
|
T, typename std::enable_if<
|
||||||
|
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
||||||
|
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
||||||
|
static T fromJni(jniType t) {
|
||||||
|
if (t == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return wrap_alias(t)->cthis();
|
||||||
|
}
|
||||||
|
// There is no automatic return conversion for objects.
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
local_ref<HybridDestructor> getHolder(T t) {
|
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {
|
||||||
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
|
static_assert(std::is_same<T, void>::value,
|
||||||
return t->getFieldValue(holderField);
|
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. "
|
||||||
}
|
"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.");
|
||||||
|
using Repr = T;
|
||||||
// JavaClass for HybridClassBase
|
};
|
||||||
struct HybridClassBase : public JavaClass<HybridClassBase> {
|
|
||||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;";
|
|
||||||
|
|
||||||
static bool isHybridClassBase(alias_ref<jclass> jclass) {
|
|
||||||
return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Base, typename Enabled = void>
|
|
||||||
struct HybridTraits {
|
|
||||||
// This static assert should actually always fail if we don't use one of the
|
|
||||||
// specializations below.
|
|
||||||
static_assert(
|
|
||||||
std::is_base_of<JObject, Base>::value ||
|
|
||||||
std::is_base_of<BaseHybridClass, Base>::value,
|
|
||||||
"The base of a HybridClass must be either another HybridClass or derived from JObject.");
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct HybridTraits<BaseHybridClass> {
|
|
||||||
using CxxBase = BaseHybridClass;
|
|
||||||
using JavaBase = JObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Base>
|
|
||||||
struct HybridTraits<
|
|
||||||
Base,
|
|
||||||
typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {
|
|
||||||
using CxxBase = Base;
|
|
||||||
using JavaBase = typename Base::JavaPart;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Base>
|
|
||||||
struct HybridTraits<
|
|
||||||
Base,
|
|
||||||
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
|
|
||||||
using CxxBase = BaseHybridClass;
|
|
||||||
using JavaBase = Base;
|
|
||||||
};
|
|
||||||
|
|
||||||
// convert to HybridClass* from jhybridobject
|
|
||||||
template <typename T>
|
|
||||||
struct Convert<
|
|
||||||
T, typename std::enable_if<
|
|
||||||
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
|
||||||
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
|
||||||
static T fromJni(jniType t) {
|
|
||||||
if (t == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return wrap_alias(t)->cthis();
|
|
||||||
}
|
|
||||||
// There is no automatic return conversion for objects.
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {
|
|
||||||
static_assert(std::is_same<T, void>::value,
|
|
||||||
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. "
|
|
||||||
"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.");
|
|
||||||
using Repr = T;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base = detail::BaseHybridClass>
|
template <typename T, typename Base = detail::BaseHybridClass>
|
||||||
class HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
||||||
public:
|
public:
|
||||||
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
||||||
// At this point, T is incomplete, and so we cannot access
|
// At this point, T is incomplete, and so we cannot access
|
||||||
// T::kJavaDescriptor directly. jtype_traits support this escape hatch for
|
// T::kJavaDescriptor directly. jtype_traits support this escape hatch for
|
||||||
// such a case.
|
// such a case.
|
||||||
static constexpr const char* kJavaDescriptor = nullptr;
|
static constexpr const char* kJavaDescriptor = nullptr;
|
||||||
static std::string get_instantiated_java_descriptor();
|
static std::string get_instantiated_java_descriptor();
|
||||||
static std::string get_instantiated_base_name();
|
static std::string get_instantiated_base_name();
|
||||||
|
|
||||||
using HybridType = T;
|
using HybridType = T;
|
||||||
|
|
||||||
// This will reach into the java object and extract the C++ instance from
|
// This will reach into the java object and extract the C++ instance from
|
||||||
// the mHybridData and return it.
|
// the mHybridData and return it.
|
||||||
T* cthis();
|
T* cthis();
|
||||||
|
|
||||||
friend class HybridClass;
|
friend class HybridClass;
|
||||||
friend T;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
using jhybridobject = typename JavaPart::javaobject;
|
using jhybridobject = typename JavaPart::javaobject;
|
||||||
using javaobject = typename JavaPart::javaobject;
|
using javaobject = typename JavaPart::javaobject;
|
||||||
typedef detail::HybridData::javaobject jhybriddata;
|
typedef detail::HybridData::javaobject jhybriddata;
|
||||||
|
|
||||||
static alias_ref<JClass> javaClassStatic() {
|
static alias_ref<JClass> javaClassStatic() {
|
||||||
return JavaPart::javaClassStatic();
|
return JavaPart::javaClassStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
static local_ref<JClass> javaClassLocal() {
|
static local_ref<JClass> javaClassLocal() {
|
||||||
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
|
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
|
||||||
return findClassLocal(className.c_str());
|
return findClassLocal(className.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef HybridClass HybridBase;
|
typedef HybridClass HybridBase;
|
||||||
|
|
||||||
// This ensures that a C++ hybrid part cannot be created on its own
|
// This ensures that a C++ hybrid part cannot be created on its own
|
||||||
// by default. If a hybrid wants to enable this, it can provide its
|
// by default. If a hybrid wants to enable this, it can provide its
|
||||||
// own public ctor, or change the accessibility of this to public.
|
// own public ctor, or change the accessibility of this to public.
|
||||||
using detail::HybridTraits<Base>::CxxBase::CxxBase;
|
using detail::HybridTraits<Base>::CxxBase::CxxBase;
|
||||||
|
|
||||||
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
|
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
|
||||||
javaClassStatic()->registerNatives(methods);
|
javaClassStatic()->registerNatives(methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
||||||
auto hybridData = detail::HybridData::create();
|
auto hybridData = detail::HybridData::create();
|
||||||
setNativePointer(hybridData, std::move(cxxPart));
|
hybridData->setNativePointer(std::move(cxxPart));
|
||||||
return hybridData;
|
return hybridData;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
|
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
|
||||||
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
public:
|
||||||
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
|
// Factory method for creating a hybrid object where the arguments
|
||||||
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
// are used to initialize the C++ part directly without passing them
|
||||||
}
|
// through java. This method requires the Java part to have a ctor
|
||||||
|
// which takes a HybridData, and for the C++ part to have a ctor
|
||||||
public:
|
// compatible with the arguments passed here. For safety, the ctor
|
||||||
// Factory method for creating a hybrid object where the arguments
|
// can be private, and the hybrid declared a friend of its base, so
|
||||||
// are used to initialize the C++ part directly without passing them
|
// the hybrid can only be created from here.
|
||||||
// through java. This method requires the Java part to have a ctor
|
//
|
||||||
// which takes a HybridData, and for the C++ part to have a ctor
|
// Exception behavior: This can throw an exception if creating the
|
||||||
// compatible with the arguments passed here. For safety, the ctor
|
// C++ object fails, or any JNI methods throw.
|
||||||
// can be private, and the hybrid declared a friend of its base, so
|
template <typename... Args>
|
||||||
// the hybrid can only be created from here.
|
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
||||||
//
|
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||||
// Exception behavior: This can throw an exception if creating the
|
return JavaPart::newInstance(hybridData);
|
||||||
// C++ object fails, or any JNI methods throw.
|
}
|
||||||
template <typename... Args>
|
|
||||||
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
|
||||||
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
|
|
||||||
auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
||||||
|
|
||||||
local_ref<JavaPart> result;
|
|
||||||
if (isHybrid) {
|
|
||||||
result = JavaPart::newInstance();
|
|
||||||
setNativePointer(result, std::move(cxxPart));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto hybridData = makeHybridData(std::move(cxxPart));
|
|
||||||
result = JavaPart::newInstance(hybridData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO? Create reusable interface for Allocatable classes and use it to
|
// TODO? Create reusable interface for Allocatable classes and use it to
|
||||||
// strengthen type-checking (and possibly provide a default
|
// strengthen type-checking (and possibly provide a default
|
||||||
// implementation of allocate().)
|
// implementation of allocate().)
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
|
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
|
||||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||||
static auto allocateMethod =
|
static auto allocateMethod =
|
||||||
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
|
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
|
||||||
return allocateMethod(javaClassStatic(), hybridData.get());
|
return allocateMethod(javaClassStatic(), hybridData.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory method for creating a hybrid object where the arguments
|
// Factory method for creating a hybrid object where the arguments
|
||||||
// are passed to the java ctor.
|
// are passed to the java ctor.
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
|
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
|
||||||
return JavaPart::newInstance(std::move(args)...);
|
return JavaPart::newInstance(std::move(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a hybrid class throws an exception which derives from
|
// If a hybrid class throws an exception which derives from
|
||||||
// std::exception, it will be passed to mapException on the hybrid
|
// std::exception, it will be passed to mapException on the hybrid
|
||||||
// class, or nearest ancestor. This allows boilerplate exception
|
// class, or nearest ancestor. This allows boilerplate exception
|
||||||
// translation code (for example, calling throwNewJavaException on a
|
// translation code (for example, calling throwNewJavaException on a
|
||||||
// particular java class) to be hoisted to a common function. If
|
// particular java class) to be hoisted to a common function. If
|
||||||
// mapException returns, then the std::exception will be translated
|
// mapException returns, then the std::exception will be translated
|
||||||
// to Java.
|
// to Java.
|
||||||
static void mapException(const std::exception& ex) {}
|
static void mapException(const std::exception& ex) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename B>
|
template <typename T, typename B>
|
||||||
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
||||||
detail::BaseHybridClass* result = 0;
|
static auto field =
|
||||||
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
|
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||||
if (isHybrid) {
|
auto hybridData = this->getFieldValue(field);
|
||||||
result = getNativePointer(this);
|
if (!hybridData) {
|
||||||
} else {
|
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||||
static auto field =
|
}
|
||||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||||
auto hybridData = this->getFieldValue(field);
|
T* value = static_cast<T*>(hybridData->getNativePointer());
|
||||||
if (!hybridData) {
|
// This would require some serious programmer error.
|
||||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
|
||||||
}
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
result = getNativePointer(hybridData);
|
template <typename T, typename B>
|
||||||
}
|
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
|
||||||
|
return T::kJavaDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
template <typename T, typename B>
|
||||||
return static_cast<T*>(result);
|
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
|
||||||
};
|
auto name = get_instantiated_java_descriptor();
|
||||||
|
return name.substr(1, name.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename B>
|
// Given a *_ref object which refers to a hybrid class, this will reach inside
|
||||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
|
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
|
||||||
return T::kJavaDescriptor;
|
// the appropriate type, and return it.
|
||||||
}
|
template <typename T>
|
||||||
|
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
|
||||||
|
return jthis->cthis();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename B>
|
void HybridDataOnLoad();
|
||||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
|
|
||||||
auto name = get_instantiated_java_descriptor();
|
|
||||||
return name.substr(1, name.size() - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a *_ref object which refers to a hybrid class, this will reach inside
|
}
|
||||||
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
|
}
|
||||||
// the appropriate type, and return it.
|
|
||||||
template <typename T>
|
|
||||||
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
|
|
||||||
return jthis->cthis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HybridDataOnLoad();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fb/fbjni.h"
|
#include "fb/fbjni.h"
|
||||||
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
|
|||||||
Reference in New Issue
Block a user