Updated fbjni since its outdated

This commit is contained in:
Pritesh Nandgaonkar
2018-06-14 22:23:11 +01:00
parent 51c36cf2bd
commit c179078acd
3 changed files with 247 additions and 191 deletions

View File

@@ -32,13 +32,13 @@ target_include_directories(${PACKAGE_NAME} PUBLIC "./")
set(libjnihack_DIR ${CMAKE_SOURCE_DIR}/../libs/jni-hack/) set(libjnihack_DIR ${CMAKE_SOURCE_DIR}/../libs/jni-hack/)
set(libfbjni_DIR ${CMAKE_SOURCE_DIR}/../libs/fbjni/src/main/cpp/include/) set(libfbjni_DIR ${CMAKE_SOURCE_DIR}/../libs/fbjni/src/main/cpp/include/)
set(libsonar_DIR ${CMAKE_SOURCE_DIR}/../xplat/) set(libsonar_DIR ${CMAKE_SOURCE_DIR}/../xplat/)
set(third_party_ndk build/third-party-ndk) set(third_party_ndk ${PROJECT_SOURCE_DIR}/build/third-party-ndk)
set(libfolly_DIR ${third_party_ndk}/folly/) set(libfolly_DIR ${third_party_ndk}/folly/)
set(glog_DIR ${third_party_ndk}/glog) set(glog_DIR ${third_party_ndk}/glog)
set(BOOST_DIR ${third_party_ndk}/boost/boost_1_63_0/) set(BOOST_DIR ${third_party_ndk}/boost/boost_1_63_0/)
set(LIBEVENT_DIR ${third_party_ndk}/LibEvent/libevent-1.0.0/include/) set(LIBEVENT_DIR ${third_party_ndk}/LibEvent/libevent-release-2.1.9/)
message(STATUS "AAAANDROID LIBEVENT_DIR => " ${LIBEVENT_DIR})
set(build_DIR ${CMAKE_SOURCE_DIR}/build) set(build_DIR ${CMAKE_SOURCE_DIR}/build)
set(fbjni_build_DIR ${build_DIR}/fbjni/${ANDROID_ABI}) set(fbjni_build_DIR ${build_DIR}/fbjni/${ANDROID_ABI})
@@ -60,7 +60,9 @@ target_include_directories(${PACKAGE_NAME} PRIVATE
${glog_DIR}/glog-0.3.5/src/ ${glog_DIR}/glog-0.3.5/src/
${BOOST_DIR} ${BOOST_DIR}
${BOOST_DIR}/../ ${BOOST_DIR}/../
${LIBEVENT_DIR} ${LIBEVENT_DIR}/
${LIBEVENT_DIR}/include/
${LIBEVENT_DIR}/include/event2
) )
target_link_libraries(${PACKAGE_NAME} fb sonarcpp) target_link_libraries(${PACKAGE_NAME} fb sonarcpp)

View File

@@ -5,227 +5,281 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
#pragma once
#include <memory> #pragma once
#include <type_traits>
#include <fb/assert.h> #include <memory>
#include <fb/visibility.h> #include <type_traits>
#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 FBEXPORT HybridData : public JavaClass<HybridData> { struct HybridData : public JavaClass<HybridData> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value); static local_ref<HybridData> create();
BaseHybridClass* getNativePointer(); };
static local_ref<HybridData> create();
};
template <typename Base, typename Enabled = void> class HybridDestructor : public JavaClass<HybridDestructor> {
struct HybridTraits { public:
// This static assert should actually always fail if we don't use one of the static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
// 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 <> detail::BaseHybridClass* getNativePointer();
struct HybridTraits<BaseHybridClass> {
using CxxBase = BaseHybridClass;
using JavaBase = JObject;
};
template <typename Base> void setNativePointer(std::unique_ptr<detail::BaseHybridClass> new_value);
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> template<typename T>
struct HybridTraits< detail::BaseHybridClass* getNativePointer(T t) {
Base, return getHolder(t)->getNativePointer();
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>
template <typename T> void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
struct FBEXPORT Convert< getHolder(t)->setNativePointer(std::move(new_value));
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>
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> { local_ref<HybridDestructor> getHolder(T t) {
static_assert(std::is_same<T, void>::value, static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. " return t->getFieldValue(holderField);
"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 FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase { class 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();
hybridData->setNativePointer(std::move(cxxPart)); setNativePointer(hybridData, 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)...)));
} }
public: template <typename... Args>
// Factory method for creating a hybrid object where the arguments static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
// are used to initialize the C++ part directly without passing them setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
// 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
// compatible with the arguments passed here. For safety, the ctor public:
// can be private, and the hybrid declared a friend of its base, so // Factory method for creating a hybrid object where the arguments
// the hybrid can only be created from here. // are used to initialize the C++ part directly without passing them
// // through java. This method requires the Java part to have a ctor
// Exception behavior: This can throw an exception if creating the // which takes a HybridData, and for the C++ part to have a ctor
// C++ object fails, or any JNI methods throw. // compatible with the arguments passed here. For safety, the ctor
template <typename... Args> // can be private, and the hybrid declared a friend of its base, so
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) { // the hybrid can only be created from here.
auto hybridData = makeCxxInstance(std::forward<Args>(args)...); //
return JavaPart::newInstance(hybridData); // Exception behavior: This can throw an exception if creating the
} // 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() {
static auto field = detail::BaseHybridClass* result = 0;
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData"); static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
auto hybridData = this->getFieldValue(field); if (isHybrid) {
if (!hybridData) { result = getNativePointer(this);
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); } else {
} static auto field =
// I'd like to use dynamic_cast here, but -fno-rtti is the default. HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
T* value = static_cast<T*>(hybridData->getNativePointer()); auto hybridData = this->getFieldValue(field);
// This would require some serious programmer error. if (!hybridData) {
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field"); throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
return value; }
};
template <typename T, typename B> result = getNativePointer(hybridData);
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() { }
return T::kJavaDescriptor;
}
template <typename T, typename B> // I'd like to use dynamic_cast here, but -fno-rtti is the default.
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() { return static_cast<T*>(result);
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 template <typename T, typename B>
// of it, find the mHybridData, extract the C++ instance pointer, cast it to /* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
// the appropriate type, and return it. return T::kJavaDescriptor;
template <typename T> }
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
return jthis->cthis();
}
void HybridDataOnLoad(); template <typename T, typename B>
/* 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();
}
}

View File

@@ -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 {