diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 5cae9f6f2..6d732a3c6 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -32,13 +32,13 @@ target_include_directories(${PACKAGE_NAME} PUBLIC "./") set(libjnihack_DIR ${CMAKE_SOURCE_DIR}/../libs/jni-hack/) set(libfbjni_DIR ${CMAKE_SOURCE_DIR}/../libs/fbjni/src/main/cpp/include/) 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(glog_DIR ${third_party_ndk}/glog) 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(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/ ${BOOST_DIR} ${BOOST_DIR}/../ - ${LIBEVENT_DIR} + ${LIBEVENT_DIR}/ + ${LIBEVENT_DIR}/include/ + ${LIBEVENT_DIR}/include/event2 ) target_link_libraries(${PACKAGE_NAME} fb sonarcpp) diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h index f020826cb..e8b4d4e92 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h +++ b/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h @@ -5,227 +5,281 @@ * LICENSE file in the root directory of this source tree. */ -#pragma once -#include -#include + #pragma once -#include -#include + #include + #include -#include "CoreClasses.h" + #include "CoreClasses.h" -namespace facebook { -namespace jni { + namespace facebook { + namespace jni { -namespace detail { + namespace detail { -class BaseHybridClass { -public: - virtual ~BaseHybridClass() {} -}; + class BaseHybridClass { + public: + virtual ~BaseHybridClass() {} + }; -struct FBEXPORT HybridData : public JavaClass { - constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; - void setNativePointer(std::unique_ptr new_value); - BaseHybridClass* getNativePointer(); - static local_ref create(); -}; + struct HybridData : public JavaClass { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; + static local_ref create(); + }; -template -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::value || - std::is_base_of::value, - "The base of a HybridClass must be either another HybridClass or derived from JObject."); -}; + class HybridDestructor : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;"; -template <> -struct HybridTraits { - using CxxBase = BaseHybridClass; - using JavaBase = JObject; -}; + detail::BaseHybridClass* getNativePointer(); -template -struct HybridTraits< - Base, - typename std::enable_if::value>::type> { - using CxxBase = Base; - using JavaBase = typename Base::JavaPart; -}; + void setNativePointer(std::unique_ptr new_value); + }; -template -struct HybridTraits< - Base, - typename std::enable_if::value>::type> { - using CxxBase = BaseHybridClass; - using JavaBase = Base; -}; + template + detail::BaseHybridClass* getNativePointer(T t) { + return getHolder(t)->getNativePointer(); + } -// convert to HybridClass* from jhybridobject -template -struct FBEXPORT Convert< - T, typename std::enable_if< - std::is_base_of::type>::value>::type> { - typedef typename std::remove_pointer::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 + void setNativePointer(T t, std::unique_ptr new_value) { + getHolder(t)->setNativePointer(std::move(new_value)); + } -template -struct RefReprType::value, void>::type> { - static_assert(std::is_same::value, - "HybridFoo (where HybridFoo derives from HybridClass) is not supported in this context. " - "For an xxx_ref, you may want: xxx_ref or HybridFoo*."); - using Repr = T; -}; + template + local_ref getHolder(T t) { + static auto holderField = t->getClass()->template getField("mDestructor"); + return t->getFieldValue(holderField); + } + + // JavaClass for HybridClassBase + struct HybridClassBase : public JavaClass { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;"; + + static bool isHybridClassBase(alias_ref jclass) { + return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass); + } + }; + + template + 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::value || + std::is_base_of::value, + "The base of a HybridClass must be either another HybridClass or derived from JObject."); + }; + + template <> + struct HybridTraits { + using CxxBase = BaseHybridClass; + using JavaBase = JObject; + }; + + template + struct HybridTraits< + Base, + typename std::enable_if::value>::type> { + using CxxBase = Base; + using JavaBase = typename Base::JavaPart; + }; + + template + struct HybridTraits< + Base, + typename std::enable_if::value>::type> { + using CxxBase = BaseHybridClass; + using JavaBase = Base; + }; + + // convert to HybridClass* from jhybridobject + template + struct Convert< + T, typename std::enable_if< + std::is_base_of::type>::value>::type> { + typedef typename std::remove_pointer::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 + struct RefReprType::value, void>::type> { + static_assert(std::is_same::value, + "HybridFoo (where HybridFoo derives from HybridClass) is not supported in this context. " + "For an xxx_ref, you may want: xxx_ref or HybridFoo*."); + using Repr = T; + }; -} + } -template -class FBEXPORT HybridClass : public detail::HybridTraits::CxxBase { -public: - struct JavaPart : JavaClass::JavaBase> { - // At this point, T is incomplete, and so we cannot access - // T::kJavaDescriptor directly. jtype_traits support this escape hatch for - // such a case. - static constexpr const char* kJavaDescriptor = nullptr; - static std::string get_instantiated_java_descriptor(); - static std::string get_instantiated_base_name(); + template + class HybridClass : public detail::HybridTraits::CxxBase { + public: + struct JavaPart : JavaClass::JavaBase> { + // At this point, T is incomplete, and so we cannot access + // T::kJavaDescriptor directly. jtype_traits support this escape hatch for + // such a case. + static constexpr const char* kJavaDescriptor = nullptr; + static std::string get_instantiated_java_descriptor(); + 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 - // the mHybridData and return it. - T* cthis(); + // This will reach into the java object and extract the C++ instance from + // the mHybridData and return it. + T* cthis(); - friend class HybridClass; - }; + friend class HybridClass; + friend T; + }; - using jhybridobject = typename JavaPart::javaobject; - using javaobject = typename JavaPart::javaobject; - typedef detail::HybridData::javaobject jhybriddata; + using jhybridobject = typename JavaPart::javaobject; + using javaobject = typename JavaPart::javaobject; + typedef detail::HybridData::javaobject jhybriddata; - static alias_ref javaClassStatic() { - return JavaPart::javaClassStatic(); - } + static alias_ref javaClassStatic() { + return JavaPart::javaClassStatic(); + } - static local_ref javaClassLocal() { - std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2); - return findClassLocal(className.c_str()); - } + static local_ref javaClassLocal() { + std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2); + return findClassLocal(className.c_str()); + } -protected: - typedef HybridClass HybridBase; + protected: + typedef HybridClass HybridBase; - // 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 - // own public ctor, or change the accessibility of this to public. - using detail::HybridTraits::CxxBase::CxxBase; + // 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 + // own public ctor, or change the accessibility of this to public. + using detail::HybridTraits::CxxBase::CxxBase; - static void registerHybrid(std::initializer_list methods) { - javaClassStatic()->registerNatives(methods); - } + static void registerHybrid(std::initializer_list methods) { + javaClassStatic()->registerNatives(methods); + } - static local_ref makeHybridData(std::unique_ptr cxxPart) { - auto hybridData = detail::HybridData::create(); - hybridData->setNativePointer(std::move(cxxPart)); - return hybridData; - } + static local_ref makeHybridData(std::unique_ptr cxxPart) { + auto hybridData = detail::HybridData::create(); + setNativePointer(hybridData, std::move(cxxPart)); + return hybridData; + } - template - static local_ref makeCxxInstance(Args&&... args) { - return makeHybridData(std::unique_ptr(new T(std::forward(args)...))); - } + template + static local_ref makeCxxInstance(Args&&... args) { + return makeHybridData(std::unique_ptr(new T(std::forward(args)...))); + } -public: - // Factory method for creating a hybrid object where the arguments - // 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 - // compatible with the arguments passed here. For safety, the ctor - // can be private, and the hybrid declared a friend of its base, so - // the hybrid can only be created from here. - // - // Exception behavior: This can throw an exception if creating the - // C++ object fails, or any JNI methods throw. - template - static local_ref newObjectCxxArgs(Args&&... args) { - auto hybridData = makeCxxInstance(std::forward(args)...); - return JavaPart::newInstance(hybridData); - } + template + static void setCxxInstance(alias_ref o, Args&&... args) { + setNativePointer(o, std::unique_ptr(new T(std::forward(args)...))); + } + + public: + // Factory method for creating a hybrid object where the arguments + // 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 + // compatible with the arguments passed here. For safety, the ctor + // can be private, and the hybrid declared a friend of its base, so + // the hybrid can only be created from here. + // + // Exception behavior: This can throw an exception if creating the + // C++ object fails, or any JNI methods throw. + template + static local_ref newObjectCxxArgs(Args&&... args) { + static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic()); + auto cxxPart = std::unique_ptr(new T(std::forward(args)...)); + + local_ref 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 - // strengthen type-checking (and possibly provide a default - // implementation of allocate().) - template - static local_ref allocateWithCxxArgs(Args&&... args) { - auto hybridData = makeCxxInstance(std::forward(args)...); - static auto allocateMethod = - javaClassStatic()->template getStaticMethod("allocate"); - return allocateMethod(javaClassStatic(), hybridData.get()); - } + // strengthen type-checking (and possibly provide a default + // implementation of allocate().) + template + static local_ref allocateWithCxxArgs(Args&&... args) { + auto hybridData = makeCxxInstance(std::forward(args)...); + static auto allocateMethod = + javaClassStatic()->template getStaticMethod("allocate"); + return allocateMethod(javaClassStatic(), hybridData.get()); + } - // Factory method for creating a hybrid object where the arguments - // are passed to the java ctor. - template - static local_ref newObjectJavaArgs(Args&&... args) { - return JavaPart::newInstance(std::move(args)...); - } + // Factory method for creating a hybrid object where the arguments + // are passed to the java ctor. + template + static local_ref newObjectJavaArgs(Args&&... args) { + return JavaPart::newInstance(std::move(args)...); + } - // If a hybrid class throws an exception which derives from - // std::exception, it will be passed to mapException on the hybrid - // class, or nearest ancestor. This allows boilerplate exception - // translation code (for example, calling throwNewJavaException on a - // particular java class) to be hoisted to a common function. If - // mapException returns, then the std::exception will be translated - // to Java. - static void mapException(const std::exception& ex) {} -}; + // If a hybrid class throws an exception which derives from + // std::exception, it will be passed to mapException on the hybrid + // class, or nearest ancestor. This allows boilerplate exception + // translation code (for example, calling throwNewJavaException on a + // particular java class) to be hoisted to a common function. If + // mapException returns, then the std::exception will be translated + // to Java. + static void mapException(const std::exception& ex) {} + }; -template -inline T* HybridClass::JavaPart::cthis() { - static auto field = - HybridClass::JavaPart::javaClassStatic()->template getField("mHybridData"); - auto hybridData = this->getFieldValue(field); - if (!hybridData) { - throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); - } - // I'd like to use dynamic_cast here, but -fno-rtti is the default. - T* value = static_cast(hybridData->getNativePointer()); - // This would require some serious programmer error. - FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field"); - return value; -}; + template + inline T* HybridClass::JavaPart::cthis() { + detail::BaseHybridClass* result = 0; + static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass()); + if (isHybrid) { + result = getNativePointer(this); + } else { + static auto field = + HybridClass::JavaPart::javaClassStatic()->template getField("mHybridData"); + auto hybridData = this->getFieldValue(field); + if (!hybridData) { + throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); + } -template -/* static */ inline std::string HybridClass::JavaPart::get_instantiated_java_descriptor() { - return T::kJavaDescriptor; -} + result = getNativePointer(hybridData); + } -template -/* static */ inline std::string HybridClass::JavaPart::get_instantiated_base_name() { - auto name = get_instantiated_java_descriptor(); - return name.substr(1, name.size() - 2); -} + // I'd like to use dynamic_cast here, but -fno-rtti is the default. + return static_cast(result); + }; -// 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 -inline auto cthis(T jthis) -> decltype(jthis->cthis()) { - return jthis->cthis(); -} + template + /* static */ inline std::string HybridClass::JavaPart::get_instantiated_java_descriptor() { + return T::kJavaDescriptor; + } -void HybridDataOnLoad(); + template + /* static */ inline std::string HybridClass::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 + inline auto cthis(T jthis) -> decltype(jthis->cthis()) { + return jthis->cthis(); + } + + void HybridDataOnLoad(); + + } + } diff --git a/libs/fbjni/src/main/cpp/jni/Hybrid.cpp b/libs/fbjni/src/main/cpp/jni/Hybrid.cpp index 523e2fd27..049e4a296 100644 --- a/libs/fbjni/src/main/cpp/jni/Hybrid.cpp +++ b/libs/fbjni/src/main/cpp/jni/Hybrid.cpp @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#include "fb/fbjni.h" + #include "fb/fbjni.h" namespace facebook {