diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h b/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h index 24d99962..8b2ba839 100644 --- a/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h +++ b/lib/fb/src/main/cpp/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/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h index 59a7f52e..f5fdc02d 100644 --- a/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h @@ -181,6 +181,27 @@ inline std::string makeDescriptor(R (C::*)(Args... args)) { return jmethod_traits_from_cxx::descriptor(); } +template +template +R CriticalMethod::call( + alias_ref, + Args... args) noexcept { + 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/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h b/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h index 39960241..74cd7b40 100644 --- a/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h @@ -58,6 +58,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) noexcept; + + template + inline static std::string desc(); +}; + } // We have to use macros here, because the func needs to be used @@ -80,6 +92,36 @@ 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/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h b/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h index d5bccf70..c38a90b3 100644 --- a/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h +++ b/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h @@ -69,6 +69,24 @@ 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 :