Support `!`-style fast calls for Android
Summary: Adds support for `!`-style fast calls in Android versions < 8. For now, this comes with the following restrictions: - has to be enabled with a macro - only supports native functions that return and accept primitive Java types (`jint`, `jdouble`, etc.), and `void` - this is supposed to map to `CriticalNative` as described in https://source.android.com/devices/tech/dalvik/improvements#faster-native-methods. Inline documentation in art: http://androidxref.com/6.0.1_r10/xref/art/runtime/jni_internal.cc#2110 Possible follow ups: - don’t prefix when the Android runtime is too new (to avoid the warning, and future error, see http://androidxref.com/8.1.0_r33/xref/art/runtime/jni_internal.cc#2355) - shim `CriticalNative` (see https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java) - test whether we can use `CriticalNative` at all (docs claim classes must be on bootclasspath) - suppport `FastNative`, i.e. instance methods that can also receive and return `jobject` Reviewed By: cjhopman Differential Revision: D9630538 fbshipit-source-id: 0ae86c909b192429d60f8eddb15528cc38610379
This commit is contained in:
parent
d7b34dd38d
commit
5f31a1082b
|
@ -235,11 +235,18 @@ class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {
|
|||
/// makeNativeMethod("nativeMethodWithExplicitDescriptor",
|
||||
/// "(Lcom/facebook/example/MyClass;)V",
|
||||
/// methodWithExplicitDescriptor),
|
||||
/// makeCriticalNativeMethod("criticalNativeMethodWithAutomaticDescriptor",
|
||||
/// criticalNativeMethodWithAutomaticDescriptor),
|
||||
/// makeCriticalNativeMethod("criticalNativeMethodWithExplicitDescriptor",
|
||||
/// "(IIF)Z",
|
||||
/// criticalNativeMethodWithExplicitDescriptor),
|
||||
/// });
|
||||
///
|
||||
/// By default, C++ exceptions raised will be converted to Java exceptions.
|
||||
/// To avoid this and get the "standard" JNI behavior of a crash when a C++
|
||||
/// exception is crashing out of the JNI method, declare the method noexcept.
|
||||
/// This does NOT apply to critical native methods, where exceptions causes
|
||||
/// a crash.
|
||||
void registerNatives(std::initializer_list<NativeMethod> methods);
|
||||
|
||||
/// Check to see if the class is assignable from another class
|
||||
|
|
|
@ -142,6 +142,25 @@ inline std::string makeDescriptor(R (C::*)(Args... args)) {
|
|||
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
template<R(*func)(Args...)>
|
||||
R CriticalMethod<R(*)(Args...)>::call(alias_ref<jclass>, Args... args) {
|
||||
static_assert(
|
||||
IsJniPrimitive<R>() || std::is_void<R>(),
|
||||
"Critical Native Methods may only return primitive JNI types, or void.");
|
||||
static_assert(
|
||||
AreJniPrimitives<Args...>(),
|
||||
"Critical Native Methods may only use primitive JNI types as parameters");
|
||||
|
||||
return func(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
template<R(*func)(Args...)>
|
||||
inline std::string CriticalMethod<R(*)(Args...)>::desc() {
|
||||
return makeDescriptor(call<func>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -44,6 +44,18 @@ std::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));
|
|||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (C::*method0)(Args... args));
|
||||
|
||||
template<typename F>
|
||||
struct CriticalMethod;
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct CriticalMethod<R(*)(Args...)> {
|
||||
template<R(*func)(Args...)>
|
||||
static R call(alias_ref<jclass>, Args... args);
|
||||
|
||||
template<R(*func)(Args...)>
|
||||
inline static std::string desc();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// We have to use macros here, because the func needs to be used
|
||||
|
@ -66,6 +78,35 @@ std::string makeDescriptor(R (C::*method0)(Args... args));
|
|||
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
|
||||
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||
|
||||
|
||||
// FAST CALLS / CRITICAL CALLS
|
||||
// Android up to and including v7 supports "fast calls" by prefixing the method
|
||||
// signature with an exclamation mark.
|
||||
// Android v8+ supports fast calls by annotating methods:
|
||||
// https://source.android.com/devices/tech/dalvik/improvements#faster-native-methods
|
||||
|
||||
// prefixes a JNI method signature as android "fast call".
|
||||
#if defined(__ANDROID__) && defined(FBJNI_WITH_FAST_CALLS)
|
||||
#define FBJNI_PREFIX_FAST_CALL(desc) (std::string{"!"} + desc)
|
||||
#else
|
||||
#define FBJNI_PREFIX_FAST_CALL(desc) (desc)
|
||||
#endif
|
||||
|
||||
#define makeCriticalNativeMethod3(name, desc, func) \
|
||||
makeNativeMethod3( \
|
||||
name, \
|
||||
FBJNI_PREFIX_FAST_CALL(desc), \
|
||||
::facebook::jni::detail::CriticalMethod<decltype(&func)>::call<&func>)
|
||||
|
||||
#define makeCriticalNativeMethod2(name, func) \
|
||||
makeCriticalNativeMethod3( \
|
||||
name, \
|
||||
::facebook::jni::detail::CriticalMethod<decltype(&func)>::desc<&func>(), \
|
||||
func)
|
||||
|
||||
#define makeCriticalNativeMethodN(a, b, c, count, ...) makeCriticalNativeMethod ## count
|
||||
#define makeCriticalNativeMethod(...) makeCriticalNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||
|
||||
}}
|
||||
|
||||
#include "Registration-inl.h"
|
||||
|
|
|
@ -69,6 +69,25 @@ constexpr bool IsJniPrimitive() {
|
|||
return is_jni_primitive<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a series of types are all primitive JNI types.
|
||||
template<typename ...Ts>
|
||||
struct are_jni_primitives;
|
||||
|
||||
template<typename T, typename ...Ts>
|
||||
struct are_jni_primitives<T, Ts...> :
|
||||
std::integral_constant<bool,
|
||||
is_jni_primitive<T>::value && are_jni_primitives<Ts...>::value> {};
|
||||
|
||||
template<>
|
||||
struct are_jni_primitives<> : std::integral_constant<bool, true> {};
|
||||
|
||||
/// Helper to simplify use of are_jni_primitives
|
||||
template<typename ...Ts>
|
||||
constexpr bool AreJniPrimitives() {
|
||||
return are_jni_primitives<Ts...>::value;
|
||||
}
|
||||
|
||||
|
||||
/// Metafunction to determine whether a type is a JNI array of primitives or not
|
||||
template <typename T>
|
||||
struct is_jni_primitive_array :
|
||||
|
|
Loading…
Reference in New Issue