Sync fbjni to React Native
Reviewed By: mhorowitz Differential Revision: D5086537 fbshipit-source-id: a95863113b3c63530a2550d29dfdc9626be86dc0
This commit is contained in:
parent
c6dd3d137b
commit
99f8c5df37
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
# include <sys/system_properties.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace build {
|
||||
|
||||
struct Build {
|
||||
static int getAndroidSdk() {
|
||||
static auto android_sdk = ([] {
|
||||
char sdk_version_str[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.build.version.sdk", sdk_version_str);
|
||||
return atoi(sdk_version_str);
|
||||
})();
|
||||
return android_sdk;
|
||||
}
|
||||
};
|
||||
|
||||
} // build
|
||||
} // facebook
|
|
@ -17,6 +17,10 @@
|
|||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace internal {
|
||||
struct CacheEnvTag {};
|
||||
}
|
||||
|
||||
// Keeps a thread-local reference to the current thread's JNIEnv.
|
||||
struct Environment {
|
||||
// May be null if this thread isn't attached to the JVM
|
||||
|
@ -70,7 +74,16 @@ class FBEXPORT ThreadScope {
|
|||
static void WithClassLoader(std::function<void()>&& runnable);
|
||||
|
||||
static void OnLoad();
|
||||
|
||||
// This constructor is only used internally by fbjni.
|
||||
ThreadScope(JNIEnv*, internal::CacheEnvTag);
|
||||
private:
|
||||
friend struct Environment;
|
||||
ThreadScope* previous_;
|
||||
// If the JNIEnv* is set, it is guaranteed to be valid at least through the
|
||||
// lifetime of this ThreadScope. The only case where that guarantee can be
|
||||
// made is when there is a java frame in the stack below this.
|
||||
JNIEnv* env_;
|
||||
bool attachedWithThisScope_;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,9 +58,12 @@ DEFINE_BOXED_PRIMITIVE(double, Double)
|
|||
|
||||
#undef DEFINE_BOXED_PRIMITIVE
|
||||
|
||||
struct JVoid : public jni::JavaClass<JVoid> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
|
||||
};
|
||||
|
||||
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
|
||||
return make_local(val);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
|
|
@ -177,7 +177,12 @@ inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods)
|
|||
|
||||
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
const auto result = env->IsAssignableFrom(self(), other.get());
|
||||
// Ths method has behavior compatible with the
|
||||
// java.lang.Class#isAssignableFrom method. The order of the
|
||||
// arguments to the JNI IsAssignableFrom C function is "opposite"
|
||||
// from what some might expect, which makes this code look a little
|
||||
// odd, but it is correct.
|
||||
const auto result = env->IsAssignableFrom(other.get(), self());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -341,13 +346,6 @@ struct Convert<const char*> {
|
|||
};
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -340,14 +340,6 @@ class FBEXPORT JString : public JavaClass<JString, JObject, jstring> {
|
|||
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);
|
||||
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
|
||||
|
||||
/// Wrapper to provide functionality to jthrowable references
|
||||
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
|
||||
|
||||
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename Target>
|
||||
class ElementProxy {
|
||||
|
@ -565,6 +557,24 @@ class PinnedPrimitiveArray {
|
|||
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
|
||||
};
|
||||
|
||||
struct JStackTraceElement : JavaClass<JStackTraceElement> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
|
||||
|
||||
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
|
||||
};
|
||||
|
||||
/// Wrapper to provide functionality to jthrowable references
|
||||
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
|
||||
|
||||
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
|
||||
|
||||
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
|
||||
local_ref<JStackTrace> getStackTrace();
|
||||
void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);
|
||||
};
|
||||
|
||||
#pragma push_macro("PlainJniRefMap")
|
||||
#undef PlainJniRefMap
|
||||
#define PlainJniRefMap(rtype, jtype) \
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
#include "References.h"
|
||||
#include "CoreClasses.h"
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)
|
||||
// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.
|
||||
#define FBJNI_NO_EXCEPTION_PTR
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
|
@ -108,9 +113,16 @@ template<typename... Args>
|
|||
}
|
||||
|
||||
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
|
||||
// be thrown, it aborts the program. This is a noexcept function at C++ level.
|
||||
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept;
|
||||
// be thrown, it aborts the program.
|
||||
FBEXPORT void translatePendingCppExceptionToJavaException();
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
|
||||
#endif
|
||||
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace();
|
||||
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
|
||||
// For convenience, some exception names in java.lang are available here.
|
||||
|
||||
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
|
||||
|
|
|
@ -29,11 +29,59 @@ public:
|
|||
|
||||
struct FBEXPORT HybridData : public JavaClass<HybridData> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
||||
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
|
||||
BaseHybridClass* getNativePointer();
|
||||
static local_ref<HybridData> create();
|
||||
};
|
||||
|
||||
class HybridDestructor : public JavaClass<HybridDestructor> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
|
||||
|
||||
template <typename T=detail::BaseHybridClass>
|
||||
T* getNativePointer() {
|
||||
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||
auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (!value) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T=detail::BaseHybridClass>
|
||||
void setNativePointer(std::unique_ptr<T> new_value) {
|
||||
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||
auto old_value = std::unique_ptr<T>(reinterpret_cast<T*>(getFieldValue(pointerField)));
|
||||
if (new_value && old_value) {
|
||||
FBCRASH("Attempt to set C++ native pointer twice");
|
||||
}
|
||||
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
detail::BaseHybridClass* getNativePointer(T t) {
|
||||
return getHolder(t)->getNativePointer();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
|
||||
getHolder(t)->setNativePointer(std::move(new_value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
local_ref<HybridDestructor> getHolder(T t) {
|
||||
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
|
||||
return t->getFieldValue(holderField);
|
||||
}
|
||||
|
||||
// JavaClass for HybridClassBase
|
||||
struct FBEXPORT 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
|
||||
|
@ -139,7 +187,7 @@ protected:
|
|||
|
||||
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
||||
auto hybridData = detail::HybridData::create();
|
||||
hybridData->setNativePointer(std::move(cxxPart));
|
||||
setNativePointer(hybridData, std::move(cxxPart));
|
||||
return hybridData;
|
||||
}
|
||||
|
||||
|
@ -148,6 +196,11 @@ protected:
|
|||
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
|
||||
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
public:
|
||||
// Factory method for creating a hybrid object where the arguments
|
||||
// are used to initialize the C++ part directly without passing them
|
||||
|
@ -161,11 +214,23 @@ public:
|
|||
// C++ object fails, or any JNI methods throw.
|
||||
template <typename... Args>
|
||||
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||
return JavaPart::newInstance(hybridData);
|
||||
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
|
||||
// implementation of allocate().)
|
||||
template <typename... Args>
|
||||
|
@ -195,17 +260,25 @@ public:
|
|||
|
||||
template <typename T, typename B>
|
||||
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
||||
static auto field =
|
||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||
auto hybridData = this->getFieldValue(field);
|
||||
if (!hybridData) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
detail::BaseHybridClass* result = 0;
|
||||
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
|
||||
if (isHybrid) {
|
||||
result = getNativePointer(this);
|
||||
} else {
|
||||
static auto field =
|
||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||
auto hybridData = this->getFieldValue(field);
|
||||
if (!hybridData) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
|
||||
result = getNativePointer(hybridData);
|
||||
}
|
||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||
T* value = static_cast<T*>(hybridData->getNativePointer());
|
||||
|
||||
// This would require some serious programmer error.
|
||||
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
|
||||
return value;
|
||||
FBASSERTMSGF(result != 0, "Incorrect C++ type in hybrid field");
|
||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||
return static_cast<T*>(result);
|
||||
};
|
||||
|
||||
template <typename T, typename B>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/**
|
||||
* Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.
|
||||
* A WeakGlobalRef can yield a strong reference even after the object has been
|
||||
* finalized. See comment in the djinni library.
|
||||
* https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp
|
||||
*/
|
||||
template<typename T = jobject>
|
||||
class JWeakReference : public JavaClass<JWeakReference<T>> {
|
||||
|
||||
typedef JavaClass<JWeakReference<T>> JavaBase_;
|
||||
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;";
|
||||
|
||||
static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {
|
||||
return JavaBase_::newInstance(static_ref_cast<jobject>(object));
|
||||
}
|
||||
|
||||
local_ref<T> get() const {
|
||||
static auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>("get");
|
||||
return static_ref_cast<T>(method(JavaBase_::self()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
#include "Boxed.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
# include <fb/Build.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
|
@ -72,12 +72,8 @@ inline bool needsSlowPath(alias_ref<jobject> obj) {
|
|||
// So, when we detect that case we must use the safe, slow workaround. That is,
|
||||
// we resolve the method id to the corresponding java.lang.reflect.Method object
|
||||
// and make the call via it's invoke() method.
|
||||
static auto android_sdk = ([] {
|
||||
char sdk_version_str[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.build.version.sdk", sdk_version_str);
|
||||
return atoi(sdk_version_str);
|
||||
})();
|
||||
static auto is_bad_android = android_sdk == 23;
|
||||
static auto is_bad_android = build::Build::getAndroidSdk() == 23;
|
||||
|
||||
if (!is_bad_android) return false;
|
||||
static auto proxy_class = findClassStatic("java/lang/reflect/Proxy");
|
||||
return obj->isInstanceOf(proxy_class);
|
||||
|
@ -100,7 +96,7 @@ inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args...
|
|||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
|
||||
#undef DEFINE_PRIMITIVE_CALL
|
||||
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \
|
||||
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
|
@ -112,14 +108,14 @@ inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args...
|
|||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean)
|
||||
DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte)
|
||||
DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter)
|
||||
DEFINE_PRIMITIVE_CALL(jshort, Short, JShort)
|
||||
DEFINE_PRIMITIVE_CALL(jint, Int, JInteger)
|
||||
DEFINE_PRIMITIVE_CALL(jlong, Long, JLong)
|
||||
DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat)
|
||||
DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble)
|
||||
DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
|
||||
|
||||
/// JMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
|
|
|
@ -95,7 +95,21 @@ template <typename T>
|
|||
struct Convert<global_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
// No automatic synthesis of global_ref
|
||||
static jniType toJniRet(global_ref<jniType> t) {
|
||||
static jniType toJniRet(global_ref<jniType>&& t) {
|
||||
// If this gets called, ownership the global_ref was passed in here. (It's
|
||||
// probably a copy of a persistent global_ref made when a function was
|
||||
// declared to return a global_ref, but it could moved out or otherwise not
|
||||
// referenced elsewhere. Doesn't matter.) Either way, the only safe way
|
||||
// to return it is to make a local_ref, release it, and return the
|
||||
// underlying local jobject.
|
||||
auto ret = make_local(t);
|
||||
return ret.release();
|
||||
}
|
||||
static jniType toJniRet(const global_ref<jniType>& t) {
|
||||
// If this gets called, the function was declared to return const&. We
|
||||
// have a ref to a global_ref whose lifetime will exceed this call, so we
|
||||
// can just get the underlying jobject and return it to java without
|
||||
// needing to make a local_ref.
|
||||
return t.get();
|
||||
}
|
||||
static jniType toCall(global_ref<jniType> t) {
|
||||
|
|
|
@ -21,7 +21,8 @@ namespace internal {
|
|||
|
||||
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
|
||||
struct ReferenceStats {
|
||||
std::atomic_uint locals_deleted, globals_deleted, weaks_deleted;
|
||||
std::atomic_uint locals_created, globals_created, weaks_created,
|
||||
locals_deleted, globals_deleted, weaks_deleted;
|
||||
|
||||
void reset() noexcept;
|
||||
};
|
||||
|
@ -35,6 +36,9 @@ extern ReferenceStats g_reference_stats;
|
|||
|
||||
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Local new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.locals_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewLocalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
|
@ -64,6 +68,9 @@ inline bool LocalReferenceAllocator::verifyReference(jobject reference) const no
|
|||
|
||||
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Global new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.globals_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
|
@ -93,6 +100,9 @@ inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const n
|
|||
|
||||
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Weak global new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.weaks_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewWeakGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
|
|
|
@ -486,22 +486,25 @@ template<typename T, typename RefType>
|
|||
auto dynamic_ref_cast(const RefType& ref) ->
|
||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
|
||||
{
|
||||
if (! ref) {
|
||||
if (!ref) {
|
||||
return decltype(static_ref_cast<T>(ref))();
|
||||
}
|
||||
|
||||
std::string target_class_name{jtype_traits<T>::base_name()};
|
||||
static alias_ref<jclass> target_class = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||
if (!target_class) {
|
||||
throwNewJavaException("java/lang/ClassCastException",
|
||||
"Could not find class %s.",
|
||||
jtype_traits<T>::base_name().c_str());
|
||||
|
||||
// If not found, will throw an exception.
|
||||
alias_ref<jclass> target_class = findClassStatic(target_class_name.c_str());
|
||||
}
|
||||
|
||||
local_ref<jclass> source_class = ref->getClass();
|
||||
|
||||
if ( ! source_class->isAssignableFrom(target_class)) {
|
||||
if (!target_class->isAssignableFrom(source_class)) {
|
||||
throwNewJavaException("java/lang/ClassCastException",
|
||||
"Tried to cast from %s to %s.",
|
||||
source_class->toString().c_str(),
|
||||
target_class_name.c_str());
|
||||
jtype_traits<T>::base_name().c_str());
|
||||
}
|
||||
|
||||
return static_ref_cast<T>(ref);
|
||||
|
|
|
@ -340,7 +340,7 @@ class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
|
|||
: base_owned_ref<T, Allocator>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
explicit weak_ref(std::nullptr_t) noexcept
|
||||
/* implicit */ weak_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Allocator>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
|
@ -409,7 +409,7 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
|
|||
: base_owned_ref<T, Alloc>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
explicit basic_strong_ref(std::nullptr_t) noexcept
|
||||
/* implicit */ basic_strong_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Alloc>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
|
@ -496,7 +496,7 @@ class alias_ref {
|
|||
alias_ref() noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
alias_ref(std::nullptr_t) noexcept;
|
||||
/* implicit */ alias_ref(std::nullptr_t) noexcept;
|
||||
|
||||
/// Copy constructor
|
||||
alias_ref(const alias_ref& other) noexcept;
|
||||
|
|
|
@ -29,141 +29,104 @@ namespace detail {
|
|||
#define JNI_ENTRY_POINT
|
||||
#endif
|
||||
|
||||
template <typename R>
|
||||
struct CreateDefault {
|
||||
static R create() {
|
||||
return R{};
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CreateDefault<void> {
|
||||
static void create() {}
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
using Converter = Convert<typename std::decay<R>::type>;
|
||||
|
||||
template <typename F, F func, typename R, typename... Args>
|
||||
struct WrapForVoidReturn {
|
||||
static typename Converter<R>::jniType call(Args&&... args) {
|
||||
return Converter<R>::toJniRet(func(std::forward<Args>(args)...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, F func, typename... Args>
|
||||
struct WrapForVoidReturn<F, func, void, Args...> {
|
||||
static void call(Args&&... args) {
|
||||
func(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
// registration wrapper for legacy JNI-style functions
|
||||
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, Args... args) {
|
||||
// Note that if func was declared noexcept, then both gcc and clang are smart
|
||||
// enough to elide the try/catch.
|
||||
try {
|
||||
(*func)(env, static_cast<C>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
struct BareJniWrapper {
|
||||
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
|
||||
ThreadScope ts(env, internal::CacheEnvTag{});
|
||||
try {
|
||||
return (*func)(env, static_cast<JniType<C>>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return CreateDefault<R>::create();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
// registration wrappers for functions, with autoconversion of arguments.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
struct FunctionWrapper {
|
||||
using jniRet = typename Converter<R>::jniType;
|
||||
JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||
ThreadScope ts(env, internal::CacheEnvTag{});
|
||||
try {
|
||||
return WrapForVoidReturn<F, func, R, JniType<C>, Args...>::call(
|
||||
static_cast<JniType<C>>(obj), Converter<Args>::fromJni(args)...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return CreateDefault<jniRet>::create();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// registration wrappers for non-static methods, with autoconvertion of arguments.
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
struct MethodWrapper {
|
||||
using jhybrid = typename C::jhybridobject;
|
||||
static R dispatch(alias_ref<jhybrid> ref, Args&&... args) {
|
||||
try {
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(ref->cthis());
|
||||
return (cobj->*method)(std::forward<Args>(args)...);
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
JNI_ENTRY_POINT static typename Converter<R>::jniType call(
|
||||
JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||
return FunctionWrapper<R(*)(alias_ref<jhybrid>, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
|
||||
try {
|
||||
return (*func)(env, static_cast<JniType<C>>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return R{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
// registration wrappers for functions, with autoconversion of arguments.
|
||||
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref<C>, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(BareJniWrapper<F, func, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
|
||||
struct funcWrapper {
|
||||
|
||||
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType call(JNIEnv*, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
return Convert<typename std::decay<R>::type>::toJniRet(
|
||||
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
|
||||
} catch (...) {
|
||||
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return jniRet{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
// registration wrappers for non-static methods, with autoconvertion of arguments.
|
||||
|
||||
template<typename M, M method, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
try {
|
||||
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(facebook::jni::cthis(aref));
|
||||
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...);
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(FunctionWrapper<F, func, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
|
||||
struct funcWrapper {
|
||||
|
||||
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType call(JNIEnv* env, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
try {
|
||||
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(facebook::jni::cthis(aref));
|
||||
return Convert<typename std::decay<R>::type>::toJniRet(
|
||||
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...));
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return jniRet{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(MethodWrapper<M, method, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
|
|
|
@ -20,28 +20,14 @@ namespace detail {
|
|||
// This uses the real JNI function as a non-type template parameter to
|
||||
// cause a (static member) function to exist with the same signature,
|
||||
// but with try/catch exception translation.
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(JNIEnv*, jobject, Args... args));
|
||||
|
||||
// Same as above, but for non-void return types.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
|
||||
|
||||
// Automatically wrap object argument, and don't take env explicitly.
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(alias_ref<C>, Args... args));
|
||||
|
||||
// Automatically wrap object argument, and don't take env explicitly,
|
||||
// non-void return type.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... args));
|
||||
|
||||
// Extract C++ instance from object, and invoke given method on it.
|
||||
template<typename M, M method, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args));
|
||||
|
||||
// Extract C++ instance from object, and invoke given method on it,
|
||||
// non-void return type
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
void FBEXPORT installTerminateHandler();
|
||||
}};
|
|
@ -65,8 +65,10 @@ public:
|
|||
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
|
||||
: env_(env)
|
||||
, javaString_(javaString)
|
||||
, length_(0)
|
||||
, utf16String_(nullptr) {
|
||||
if (env_ && javaString_) {
|
||||
length_ = env_->GetStringLength(javaString_);
|
||||
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -77,13 +79,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
operator const jchar* () const {
|
||||
const jsize length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
const jchar* chars() const {
|
||||
return utf16String_;
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv* env_;
|
||||
jstring javaString_;
|
||||
jsize length_;
|
||||
const jchar* utf16String_;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fb/log.h>
|
||||
#include <fb/StaticInitialized.h>
|
||||
#include <fb/ThreadLocal.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
|
@ -21,9 +19,143 @@ namespace facebook {
|
|||
namespace jni {
|
||||
|
||||
namespace {
|
||||
StaticInitialized<ThreadLocal<JNIEnv>> g_env;
|
||||
|
||||
ThreadLocal<ThreadScope>& scopeStorage() {
|
||||
// We don't want the ThreadLocal to delete the ThreadScopes.
|
||||
static ThreadLocal<ThreadScope> scope([] (void*) {});
|
||||
return scope;
|
||||
}
|
||||
|
||||
ThreadScope* currentScope() {
|
||||
return scopeStorage().get();
|
||||
}
|
||||
|
||||
JavaVM* g_vm = nullptr;
|
||||
|
||||
struct EnvironmentInitializer {
|
||||
EnvironmentInitializer(JavaVM* vm) {
|
||||
FBASSERT(!g_vm);
|
||||
FBASSERT(vm);
|
||||
g_vm = vm;
|
||||
}
|
||||
};
|
||||
|
||||
int getEnv(JNIEnv** env) {
|
||||
FBASSERT(g_vm);
|
||||
// g_vm->GetEnv() might not clear the env* in failure cases.
|
||||
*env = nullptr;
|
||||
return g_vm->GetEnv((void**)env, JNI_VERSION_1_6);
|
||||
}
|
||||
|
||||
JNIEnv* attachCurrentThread() {
|
||||
JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};
|
||||
JNIEnv* env = nullptr;
|
||||
auto result = g_vm->AttachCurrentThread(&env, &args);
|
||||
FBASSERT(result == JNI_OK);
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::initialize(JavaVM* vm) {
|
||||
static EnvironmentInitializer init(vm);
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::current() {
|
||||
auto scope = currentScope();
|
||||
if (scope && scope->env_) {
|
||||
return scope->env_;
|
||||
}
|
||||
|
||||
JNIEnv* env;
|
||||
if (getEnv(&env) != JNI_OK) {
|
||||
// If there's a ThreadScope in the stack, we should be attached and able to
|
||||
// retrieve a JNIEnv*.
|
||||
FBASSERT(!scope);
|
||||
|
||||
// TODO(cjhopman): this should probably be a hard failure, too.
|
||||
FBLOGE("Unable to retrieve jni environment. Is the thread attached?");
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::detachCurrentThread() {
|
||||
FBASSERT(g_vm);
|
||||
// The thread shouldn't be detached while a ThreadScope is in the stack.
|
||||
FBASSERT(!currentScope());
|
||||
g_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
|
||||
auto scope = currentScope();
|
||||
if (scope && scope->env_) {
|
||||
return scope->env_;
|
||||
}
|
||||
|
||||
JNIEnv* env;
|
||||
// We should be able to just get the JNIEnv* by just calling
|
||||
// AttachCurrentThread, but the spec is unclear (and using getEnv is probably
|
||||
// generally more reliable).
|
||||
auto result = getEnv(&env);
|
||||
// We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.
|
||||
FBASSERT(result == JNI_OK || result == JNI_EDETACHED);
|
||||
if (result == JNI_EDETACHED) {
|
||||
// The thread should not be detached while a ThreadScope is in the stack.
|
||||
FBASSERT(!scope);
|
||||
env = attachCurrentThread();
|
||||
}
|
||||
FBASSERT(env);
|
||||
return env;
|
||||
}
|
||||
|
||||
ThreadScope::ThreadScope() : ThreadScope(nullptr, internal::CacheEnvTag{}) {}
|
||||
|
||||
ThreadScope::ThreadScope(JNIEnv* env, internal::CacheEnvTag)
|
||||
: previous_(nullptr), env_(nullptr), attachedWithThisScope_(false) {
|
||||
auto& storage = scopeStorage();
|
||||
previous_ = storage.get();
|
||||
storage.reset(this);
|
||||
|
||||
if (previous_ && previous_->env_) {
|
||||
FBASSERT(!env || env == previous_->env_);
|
||||
env = previous_->env_;
|
||||
}
|
||||
|
||||
env_ = env;
|
||||
if (env_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the thread is attached by someone else.
|
||||
auto result = getEnv(&env);
|
||||
if (result == JNI_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.
|
||||
FBASSERT(result == JNI_EDETACHED);
|
||||
|
||||
// If there's already a ThreadScope on the stack, then the thread should be attached.
|
||||
FBASSERT(!previous_);
|
||||
attachCurrentThread();
|
||||
attachedWithThisScope_ = true;
|
||||
}
|
||||
|
||||
ThreadScope::~ThreadScope() {
|
||||
auto& storage = scopeStorage();
|
||||
// ThreadScopes should be destroyed in the reverse order they are created
|
||||
// (that is, just put them on the stack).
|
||||
FBASSERT(this == storage.get());
|
||||
storage.reset(previous_);
|
||||
if (attachedWithThisScope_) {
|
||||
Environment::detachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;";
|
||||
|
||||
|
@ -47,73 +179,6 @@ struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
|
|||
};
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::current() {
|
||||
JNIEnv* env = g_env->get();
|
||||
if ((env == nullptr) && (g_vm != nullptr)) {
|
||||
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
FBLOGE("Error retrieving JNI Environment, thread is probably not attached to JVM");
|
||||
// TODO(cjhopman): This should throw an exception.
|
||||
env = nullptr;
|
||||
} else {
|
||||
g_env->reset(env);
|
||||
}
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::detachCurrentThread() {
|
||||
auto env = g_env->get();
|
||||
if (env) {
|
||||
FBASSERT(g_vm);
|
||||
g_vm->DetachCurrentThread();
|
||||
g_env->reset();
|
||||
}
|
||||
}
|
||||
|
||||
struct EnvironmentInitializer {
|
||||
EnvironmentInitializer(JavaVM* vm) {
|
||||
FBASSERT(!g_vm);
|
||||
FBASSERT(vm);
|
||||
g_vm = vm;
|
||||
g_env.initialize([] (void*) {});
|
||||
}
|
||||
};
|
||||
|
||||
/* static */
|
||||
void Environment::initialize(JavaVM* vm) {
|
||||
static EnvironmentInitializer init(vm);
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
|
||||
auto env = g_env->get();
|
||||
if (!env) {
|
||||
FBASSERT(g_vm);
|
||||
g_vm->AttachCurrentThread(&env, nullptr);
|
||||
g_env->reset(env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
ThreadScope::ThreadScope()
|
||||
: attachedWithThisScope_(false) {
|
||||
JNIEnv* env = nullptr;
|
||||
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_EDETACHED) {
|
||||
return;
|
||||
}
|
||||
env = facebook::jni::Environment::ensureCurrentThreadIsAttached();
|
||||
FBASSERT(env);
|
||||
attachedWithThisScope_ = true;
|
||||
}
|
||||
|
||||
ThreadScope::~ThreadScope() {
|
||||
if (attachedWithThisScope_) {
|
||||
Environment::detachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ThreadScope::OnLoad() {
|
||||
// These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.
|
||||
|
@ -123,7 +188,8 @@ void ThreadScope::OnLoad() {
|
|||
/* static */
|
||||
void ThreadScope::WithClassLoader(std::function<void()>&& runnable) {
|
||||
// TODO(cjhopman): If the classloader is already available in this scope, we
|
||||
// shouldn't have to jump through java.
|
||||
// shouldn't have to jump through java. It should be enough to check if the
|
||||
// attach state env* is set.
|
||||
ThreadScope ts;
|
||||
JThreadScopeSupport::runStdFunction(std::move(runnable));
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#include <fb/assert.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
#ifdef USE_LYRA
|
||||
#include <fb/lyra.h>
|
||||
#include <fb/lyra_exceptions.h>
|
||||
#endif
|
||||
|
||||
#include <alloca.h>
|
||||
#include <cstdlib>
|
||||
#include <ios>
|
||||
|
@ -22,7 +27,6 @@
|
|||
|
||||
#include <jni.h>
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
|
@ -107,7 +111,13 @@ void setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {
|
|||
|
||||
// Functions that throw C++ exceptions
|
||||
|
||||
// TODO(T6618159) Take a stack dump here to save context if it results in a crash when propagated
|
||||
// TODO(T6618159) Inject the c++ stack into the exception's stack trace. One
|
||||
// issue: when a java exception is created, it captures the full java stack
|
||||
// across jni boundaries. lyra will only capture the c++ stack to the jni
|
||||
// boundary. So, as we pass the java exception up to c++, we need to capture
|
||||
// the c++ stack and then insert it into the correct place in the java stack
|
||||
// trace. Then, as the exception propagates across the boundaries, we will
|
||||
// slowly fill in the c++ parts of the trace.
|
||||
void throwPendingJniExceptionAsCppException() {
|
||||
JNIEnv* env = Environment::current();
|
||||
if (env->ExceptionCheck() == JNI_FALSE) {
|
||||
|
@ -151,82 +161,158 @@ void throwNewJavaException(const char* throwableName, const char* msg) {
|
|||
throwNewJavaException(throwable.get());
|
||||
}
|
||||
|
||||
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
|
||||
static auto meth = javaClassStatic()->getMethod<javaobject(alias_ref<javaobject>)>("initCause");
|
||||
return meth(self(), cause);
|
||||
}
|
||||
|
||||
auto JThrowable::getStackTrace() -> local_ref<JStackTrace> {
|
||||
static auto meth = javaClassStatic()->getMethod<JStackTrace::javaobject()>("getStackTrace");
|
||||
return meth(self());
|
||||
}
|
||||
|
||||
void JThrowable::setStackTrace(alias_ref<JStackTrace> stack) {
|
||||
static auto meth = javaClassStatic()->getMethod<void(alias_ref<JStackTrace>)>("setStackTrace");
|
||||
return meth(self(), stack);
|
||||
}
|
||||
|
||||
auto JStackTraceElement::create(
|
||||
const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
|
||||
-> local_ref<javaobject> {
|
||||
return newInstance(declaringClass, methodName, file, line);
|
||||
}
|
||||
|
||||
// Translate C++ to Java Exception
|
||||
|
||||
namespace {
|
||||
|
||||
// The implementation std::rethrow_if_nested uses a dynamic_cast to determine
|
||||
// if the exception is a nested_exception. If the exception is from a library
|
||||
// built with -fno-rtti, then that will crash. This avoids that.
|
||||
void rethrow_if_nested() {
|
||||
// For each exception in the chain of the exception_ptr argument, func
|
||||
// will be called with that exception (in reverse order, i.e. innermost first).
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
void denest(const std::function<void(std::exception_ptr)>& func, std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
try {
|
||||
throw;
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (const std::nested_exception& e) {
|
||||
e.rethrow_nested();
|
||||
denest(func, e.nested_ptr());
|
||||
} catch (...) {
|
||||
// ignored.
|
||||
}
|
||||
func(ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
#ifdef USE_LYRA
|
||||
local_ref<JStackTraceElement> createJStackTraceElement(const lyra::StackTraceElement& cpp) {
|
||||
return JStackTraceElement::create(
|
||||
"|lyra|{" + cpp.libraryName() + "}", cpp.functionName(), cpp.buildId(), cpp.libraryOffset());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
void addCppStacktraceToJavaException(alias_ref<JThrowable> java, std::exception_ptr cpp) {
|
||||
#ifdef USE_LYRA
|
||||
auto cppStack = lyra::getStackTraceSymbols(
|
||||
(cpp == nullptr) ?
|
||||
lyra::getStackTrace()
|
||||
: lyra::getExceptionTrace(cpp));
|
||||
|
||||
auto javaStack = java->getStackTrace();
|
||||
auto newStack = JThrowable::JStackTrace::newArray(javaStack->size() + cppStack.size());
|
||||
size_t i = 0;
|
||||
for (size_t j = 0; j < cppStack.size(); j++, i++) {
|
||||
(*newStack)[i] = createJStackTraceElement(cppStack[j]);
|
||||
}
|
||||
for (size_t j = 0; j < javaStack->size(); j++, i++) {
|
||||
(*newStack)[i] = (*javaStack)[j];
|
||||
}
|
||||
java->setStackTrace(newStack);
|
||||
#endif
|
||||
}
|
||||
|
||||
// For each exception in the chain of the currently handled exception, func
|
||||
// will be called with that exception as the currently handled exception (in
|
||||
// reverse order, i.e. innermost first).
|
||||
void denest(std::function<void()> func) {
|
||||
local_ref<JThrowable> convertCppExceptionToJavaException(std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
local_ref<JThrowable> current;
|
||||
bool addCppStack = true;
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::exception& e) {
|
||||
try {
|
||||
rethrow_if_nested();
|
||||
} catch (...) {
|
||||
denest(func);
|
||||
}
|
||||
func();
|
||||
std::rethrow_exception(ptr);
|
||||
addCppStack = false;
|
||||
} catch (const JniException& ex) {
|
||||
current = ex.getThrowable();
|
||||
} catch (const std::ios_base::failure& ex) {
|
||||
current = JIOException::create(ex.what());
|
||||
} catch (const std::bad_alloc& ex) {
|
||||
current = JOutOfMemoryError::create(ex.what());
|
||||
} catch (const std::out_of_range& ex) {
|
||||
current = JArrayIndexOutOfBoundsException::create(ex.what());
|
||||
} catch (const std::system_error& ex) {
|
||||
current = JCppSystemErrorException::create(ex);
|
||||
} catch (const std::runtime_error& ex) {
|
||||
current = JRuntimeException::create(ex.what());
|
||||
} catch (const std::exception& ex) {
|
||||
current = JCppException::create(ex.what());
|
||||
} catch (const char* msg) {
|
||||
current = JUnknownCppException::create(msg);
|
||||
} catch (...) {
|
||||
func();
|
||||
current = JUnknownCppException::create();
|
||||
}
|
||||
}
|
||||
|
||||
if (addCppStack) {
|
||||
addCppStacktraceToJavaException(current, ptr);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
#endif
|
||||
|
||||
local_ref<JThrowable> getJavaExceptionForCppBackTrace() {
|
||||
return getJavaExceptionForCppBackTrace(nullptr);
|
||||
}
|
||||
|
||||
void translatePendingCppExceptionToJavaException() noexcept {
|
||||
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg) {
|
||||
local_ref<JThrowable> current =
|
||||
msg ? JUnknownCppException::create(msg) : JUnknownCppException::create();
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
addCppStacktraceToJavaException(current, nullptr);
|
||||
#endif
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
local_ref<JThrowable> previous;
|
||||
auto func = [&previous] () {
|
||||
local_ref<JThrowable> current;
|
||||
try {
|
||||
throw;
|
||||
} catch(const JniException& ex) {
|
||||
current = ex.getThrowable();
|
||||
} catch(const std::ios_base::failure& ex) {
|
||||
current = JIOException::create(ex.what());
|
||||
} catch(const std::bad_alloc& ex) {
|
||||
current = JOutOfMemoryError::create(ex.what());
|
||||
} catch(const std::out_of_range& ex) {
|
||||
current = JArrayIndexOutOfBoundsException::create(ex.what());
|
||||
} catch(const std::system_error& ex) {
|
||||
current = JCppSystemErrorException::create(ex);
|
||||
} catch(const std::runtime_error& ex) {
|
||||
current = JRuntimeException::create(ex.what());
|
||||
} catch(const std::exception& ex) {
|
||||
current = JCppException::create(ex.what());
|
||||
} catch(const char* msg) {
|
||||
current = JUnknownCppException::create(msg);
|
||||
} catch(...) {
|
||||
current = JUnknownCppException::create();
|
||||
}
|
||||
auto func = [&previous] (std::exception_ptr ptr) {
|
||||
auto current = convertCppExceptionToJavaException(ptr);
|
||||
if (previous) {
|
||||
current->initCause(previous);
|
||||
}
|
||||
previous = current;
|
||||
};
|
||||
denest(func, ptr);
|
||||
return previous;
|
||||
}
|
||||
#endif
|
||||
|
||||
void translatePendingCppExceptionToJavaException() {
|
||||
try {
|
||||
denest(func);
|
||||
setJavaExceptionAndAbortOnFailure(previous);
|
||||
} catch (std::exception& e) {
|
||||
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException: %s", e.what());
|
||||
// rethrow the exception and let the noexcept handling abort.
|
||||
throw;
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
auto exc = getJavaExceptionForCppException(std::current_exception());
|
||||
#else
|
||||
auto exc = JUnknownCppException::create();
|
||||
#endif
|
||||
setJavaExceptionAndAbortOnFailure(exc);
|
||||
} catch (...) {
|
||||
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException");
|
||||
throw;
|
||||
#ifdef USE_LYRA
|
||||
FBLOGE("Unexpected error in translatePendingCppExceptionToJavaException(): %s",
|
||||
lyra::toString(std::current_exception()).c_str());
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,8 +338,13 @@ JniException::JniException(const JniException &rhs)
|
|||
}
|
||||
|
||||
JniException::~JniException() {
|
||||
ThreadScope ts;
|
||||
throwable_.reset();
|
||||
try {
|
||||
ThreadScope ts;
|
||||
throwable_.reset();
|
||||
} catch (...) {
|
||||
FBLOGE("Exception in ~JniException()");
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
local_ref<JThrowable> JniException::getThrowable() const noexcept {
|
||||
|
@ -262,8 +353,8 @@ local_ref<JThrowable> JniException::getThrowable() const noexcept {
|
|||
|
||||
// TODO 6900503: consider making this thread-safe.
|
||||
void JniException::populateWhat() const noexcept {
|
||||
ThreadScope ts;
|
||||
try {
|
||||
ThreadScope ts;
|
||||
what_ = throwable_->toString();
|
||||
isMessageExtracted_ = true;
|
||||
} catch(...) {
|
||||
|
|
|
@ -15,35 +15,6 @@ namespace jni {
|
|||
|
||||
namespace detail {
|
||||
|
||||
void HybridData::setNativePointer(std::unique_ptr<BaseHybridClass> new_value) {
|
||||
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
|
||||
auto* old_value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (new_value) {
|
||||
// Modify should only ever be called once with a non-null
|
||||
// new_value. If this happens again it's a programmer error, so
|
||||
// blow up.
|
||||
FBASSERTMSGF(old_value == 0, "Attempt to set C++ native pointer twice");
|
||||
} else if (old_value == 0) {
|
||||
return;
|
||||
}
|
||||
// delete on a null pointer is defined to be a noop.
|
||||
delete old_value;
|
||||
// This releases ownership from the unique_ptr, and passes the pointer, and
|
||||
// ownership of it, to HybridData which is managed by the java GC. The
|
||||
// finalizer on hybridData calls resetNative which will delete the object, if
|
||||
// resetNative has not already been called.
|
||||
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
|
||||
}
|
||||
|
||||
BaseHybridClass* HybridData::getNativePointer() {
|
||||
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
|
||||
auto* value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (!value) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
local_ref<HybridData> HybridData::create() {
|
||||
return newInstance();
|
||||
}
|
||||
|
@ -51,14 +22,14 @@ local_ref<HybridData> HybridData::create() {
|
|||
}
|
||||
|
||||
namespace {
|
||||
void resetNative(alias_ref<detail::HybridData> jthis) {
|
||||
jthis->setNativePointer(nullptr);
|
||||
void deleteNative(alias_ref<jclass>, jlong ptr) {
|
||||
delete reinterpret_cast<detail::BaseHybridClass*>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void HybridDataOnLoad() {
|
||||
registerNatives("com/facebook/jni/HybridData", {
|
||||
makeNativeMethod("resetNative", resetNative),
|
||||
registerNatives("com/facebook/jni/HybridData$Destructor", {
|
||||
makeNativeMethod("deleteNative", deleteNative),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -305,8 +305,7 @@ LocalString::~LocalString() {
|
|||
|
||||
std::string fromJString(JNIEnv* env, jstring str) {
|
||||
auto utf16String = JStringUtf16Extractor(env, str);
|
||||
auto length = env->GetStringLength(str);
|
||||
return detail::utf16toUTF8(utf16String, length);
|
||||
return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2016-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.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <jni/GlobalReference.h>
|
||||
#include <jni/NativeSoftError.h>
|
||||
#include <jni/LocalString.h>
|
||||
#include <jni/Registration.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
namespace softerror {
|
||||
|
||||
using facebook::jni::findClassStatic;
|
||||
using facebook::jni::LocalString;
|
||||
|
||||
static alias_ref<jclass> softErrorClass_;
|
||||
|
||||
bool checkSoftErrorClassRef() {
|
||||
if (!softErrorClass_) {
|
||||
softErrorClass_ = findClassStatic("com/facebook/jni/NativeSoftErrorReporterProxy");
|
||||
}
|
||||
|
||||
return softErrorClass_ ? true : false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void reportSoftErrorInternal(
|
||||
Severity severity,
|
||||
const char* category,
|
||||
const char* error_msg,
|
||||
#ifndef JNI_NO_EXCEPTION_PTR
|
||||
std::exception_ptr cause,
|
||||
#endif
|
||||
unsigned int samplingFrequency) {
|
||||
// Need to ensure we have access to our classes even in an unattached thread.
|
||||
ThreadScope::WithClassLoader([&] {
|
||||
if (!checkSoftErrorClassRef()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static auto softReport =
|
||||
softErrorClass_->getStaticMethod<void(jint, jstring, jstring, jthrowable, jint)>("softReport");
|
||||
LocalString jstrCategory(category);
|
||||
LocalString jstrErrorMsg(error_msg);
|
||||
softReport(softErrorClass_,
|
||||
severity,
|
||||
jstrCategory.string(),
|
||||
jstrErrorMsg.string(),
|
||||
#ifndef JNI_NO_EXCEPTION_PTR
|
||||
(cause == nullptr) ? getJavaExceptionForCppBackTrace().get() : getJavaExceptionForCppException(cause).get(),
|
||||
#else
|
||||
getJavaExceptionForCppBackTrace().get(),
|
||||
#endif
|
||||
samplingFrequency);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void reportSoftError(
|
||||
Severity severity,
|
||||
const char* category,
|
||||
const char* error_msg,
|
||||
unsigned int samplingFrequency) {
|
||||
reportSoftErrorInternal(
|
||||
severity,
|
||||
category,
|
||||
error_msg,
|
||||
#ifndef JNI_NO_EXCEPTION_PTR
|
||||
nullptr,
|
||||
#endif
|
||||
samplingFrequency);
|
||||
}
|
||||
|
||||
#ifndef JNI_NO_EXCEPTION_PTR
|
||||
void FBEXPORT reportSoftError(
|
||||
Severity severity,
|
||||
const char* category,
|
||||
const char* error_msg,
|
||||
std::exception_ptr cause,
|
||||
unsigned int samplingFrequency) {
|
||||
|
||||
reportSoftErrorInternal(
|
||||
severity,
|
||||
category,
|
||||
error_msg,
|
||||
cause,
|
||||
samplingFrequency);
|
||||
}
|
||||
#endif
|
||||
|
||||
void generateNativeSoftError() {
|
||||
reportSoftError(Severity::MUST_FIX, "SoftErrorTest_1", "Reporting MUST_FIX");
|
||||
#ifndef JNI_NO_EXCEPTION_PTR
|
||||
reportSoftError(
|
||||
Severity::WARNING,
|
||||
"SoftErrorTest_2",
|
||||
"Reporting WARNING with cause",
|
||||
make_exception_ptr(std::invalid_argument("Fake exception")));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SoftErrorOnLoad(JNIEnv* env) {
|
||||
if(!checkSoftErrorClassRef()) {
|
||||
return;
|
||||
}
|
||||
|
||||
registerNatives(env, softErrorClass_.get(), {
|
||||
{ "generateNativeSoftError", "()V", (void*) generateNativeSoftError }
|
||||
});
|
||||
}
|
||||
|
||||
} } }
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <fb/CpuCapabilities.h>
|
||||
#include <cpu-features.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <glog/logging.h>
|
||||
#include <jni/Registration.h>
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
// =========================================
|
||||
// returns true if this device supports NEON calls, false otherwise
|
||||
jboolean nativeDeviceSupportsNeon(JNIEnv* env, jobject obj) {
|
||||
if (android_getCpuFamily() != ANDROID_CPU_FAMILY_ARM) {
|
||||
VLOG(2) << "NEON disabled, not an ARM CPU";
|
||||
return false;
|
||||
}
|
||||
uint64_t cpufeatures = android_getCpuFeatures();
|
||||
if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_ARMv7) == 0) {
|
||||
VLOG(2) << "NEON disabled, not an ARMv7 CPU";
|
||||
return false;
|
||||
}
|
||||
if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_NEON) == 0) {
|
||||
VLOG(2) << "NEON disabled, not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG(2) << "NEON supported and enabled";
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// returns true if this device supports VFP_FP16, false otherwise.
|
||||
jboolean nativeDeviceSupportsVFPFP16(JNIEnv *env, jobject obj) {
|
||||
uint64_t cpufeatures = android_getCpuFeatures();
|
||||
if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_VFP_FP16) == 0) {
|
||||
VLOG(2) << "VPF_FP16 disabled, not supported";
|
||||
return false;
|
||||
}
|
||||
VLOG(2) << "VFP_FP16 supported and enabled";
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// returns true if this device is x86 based, false otherwise
|
||||
jboolean nativeDeviceSupportsX86(JNIEnv* env, jobject obj) {
|
||||
return (android_getCpuFamily() == ANDROID_CPU_FAMILY_X86);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// register native methods
|
||||
void initialize_cpucapabilities() {
|
||||
facebook::jni::registerNatives(
|
||||
Environment::current(),
|
||||
"com/facebook/jni/CpuCapabilitiesJni",
|
||||
{
|
||||
{ "nativeDeviceSupportsNeon",
|
||||
"()Z",
|
||||
(void*) nativeDeviceSupportsNeon },
|
||||
{ "nativeDeviceSupportsVFPFP16",
|
||||
"()Z",
|
||||
(void*) nativeDeviceSupportsVFPFP16 },
|
||||
{ "nativeDeviceSupportsX86",
|
||||
"()Z",
|
||||
(void*) nativeDeviceSupportsX86 },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} } // namespace facebook::jni
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ANDROID__
|
||||
#error "This file should only be compiled for Android."
|
||||
#endif
|
||||
|
||||
#include <fb/fbjni/References.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
namespace internal {
|
||||
|
||||
static int32_t getApiLevel() {
|
||||
auto cls = findClassLocal("android/os/Build$VERSION");
|
||||
auto fld = cls->getStaticField<int32_t>("SDK_INT");
|
||||
if (fld) {
|
||||
return cls->getStaticFieldValue(fld);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool doesGetObjectRefTypeWork() {
|
||||
static auto level = getApiLevel();
|
||||
return level >= 14;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,9 +60,9 @@ alias_ref<JClass> findClassStatic(const char* name) {
|
|||
if (!env) {
|
||||
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
||||
}
|
||||
auto cls = env->FindClass(name);
|
||||
local_ref<jclass> cls = adopt_local(env->FindClass(name));
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
||||
auto leaking_ref = (jclass)env->NewGlobalRef(cls);
|
||||
auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
|
||||
return wrap_alias(leaking_ref);
|
||||
}
|
||||
|
@ -83,8 +83,7 @@ local_ref<JClass> findClassLocal(const char* name) {
|
|||
std::string JString::toStdString() const {
|
||||
const auto env = internal::getEnv();
|
||||
auto utf16String = JStringUtf16Extractor(env, self());
|
||||
auto length = env->GetStringLength(self());
|
||||
return detail::utf16toUTF8(utf16String, length);
|
||||
return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
|
||||
}
|
||||
|
||||
local_ref<JString> make_jstring(const char* utf8) {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
java_library(
|
||||
name = "java",
|
||||
srcs = glob(["**/*.java"]),
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
"//java/com/facebook/proguard/annotations:annotations",
|
||||
],
|
||||
)
|
Loading…
Reference in New Issue