From 5f31a1082badee7238623956258549e4c0608915 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Wed, 19 Sep 2018 17:23:10 -0700 Subject: [PATCH] Support `!`-style fast calls for Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../fb/include/fb/fbjni/CoreClasses.h | 7 ++++ .../fb/include/fb/fbjni/Registration-inl.h | 19 +++++++++ .../fb/include/fb/fbjni/Registration.h | 41 +++++++++++++++++++ .../fb/include/fb/fbjni/TypeTraits.h | 19 +++++++++ 4 files changed, 86 insertions(+) diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h index e48079b83..169c621f2 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h @@ -235,11 +235,18 @@ class FBEXPORT JClass : public JavaClass { /// 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 methods); /// Check to see if the class is assignable from another class diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration-inl.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration-inl.h index 172399a0e..5df1a12de 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration-inl.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration-inl.h @@ -142,6 +142,25 @@ inline std::string makeDescriptor(R (C::*)(Args... args)) { return jmethod_traits_from_cxx::descriptor(); } +template +template +R CriticalMethod::call(alias_ref, Args... args) { + static_assert( + IsJniPrimitive() || std::is_void(), + "Critical Native Methods may only return primitive JNI types, or void."); + static_assert( + AreJniPrimitives(), + "Critical Native Methods may only use primitive JNI types as parameters"); + + return func(std::forward(args)...); +} + +template +template +inline std::string CriticalMethod::desc() { + return makeDescriptor(call); +} + } }} diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration.h index e873ceb93..344f336f8 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Registration.h @@ -44,6 +44,18 @@ std::string makeDescriptor(R (*func)(alias_ref, Args... args)); template std::string makeDescriptor(R (C::*method0)(Args... args)); +template +struct CriticalMethod; + +template +struct CriticalMethod { + template + static R call(alias_ref, Args... args); + + template + 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::call<&func>) + +#define makeCriticalNativeMethod2(name, func) \ + makeCriticalNativeMethod3( \ + name, \ + ::facebook::jni::detail::CriticalMethod::desc<&func>(), \ + func) + +#define makeCriticalNativeMethodN(a, b, c, count, ...) makeCriticalNativeMethod ## count +#define makeCriticalNativeMethod(...) makeCriticalNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__) + }} #include "Registration-inl.h" diff --git a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/TypeTraits.h b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/TypeTraits.h index d5bccf706..e8dff3be7 100644 --- a/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/TypeTraits.h +++ b/ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/TypeTraits.h @@ -69,6 +69,25 @@ constexpr bool IsJniPrimitive() { return is_jni_primitive::value; } +/// Metafunction to determine whether a series of types are all primitive JNI types. +template +struct are_jni_primitives; + +template +struct are_jni_primitives : + std::integral_constant::value && are_jni_primitives::value> {}; + +template<> +struct are_jni_primitives<> : std::integral_constant {}; + +/// Helper to simplify use of are_jni_primitives +template +constexpr bool AreJniPrimitives() { + return are_jni_primitives::value; +} + + /// Metafunction to determine whether a type is a JNI array of primitives or not template struct is_jni_primitive_array :