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:
David Aurelio 2018-09-19 17:23:10 -07:00 committed by Facebook Github Bot
parent d7b34dd38d
commit 5f31a1082b
4 changed files with 86 additions and 0 deletions

View File

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

View File

@ -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>);
}
}
}}

View File

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

View File

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