From 3bbc4f63173798179181d147b7e6f3ecd501186e Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Fri, 8 Nov 2024 14:38:19 -0800 Subject: [PATCH] Rev JNI Bind release header to v 1.1.2. PiperOrigin-RevId: 694645676 --- JNI_BIND_VERSION.inc | 2 +- README.md | 4 +- jni_bind_release.h | 4205 +++++++++++++++++++++--------------------- 3 files changed, 2155 insertions(+), 2056 deletions(-) diff --git a/JNI_BIND_VERSION.inc b/JNI_BIND_VERSION.inc index 524cb552..45a1b3f4 100644 --- a/JNI_BIND_VERSION.inc +++ b/JNI_BIND_VERSION.inc @@ -1 +1 @@ -1.1.1 +1.1.2 diff --git a/README.md b/README.md index 8fe62594..abd18193 100755 --- a/README.md +++ b/README.md @@ -92,8 +92,8 @@ If you're already using Bazel add the following to your WORKSPACE: ```starlark http_archive( name = "jni-bind", - urls = ["https://github.com/google/jni-bind/archive/refs/tags/Release-1.1.1-beta.zip"], - strip_prefix = "jni-bind-Release-1.1.1-beta", + urls = ["https://github.com/google/jni-bind/archive/refs/tags/Release-1.1.2-beta.zip"], + strip_prefix = "jni-bind-Release-1.1.2-beta", ) ``` diff --git a/jni_bind_release.h b/jni_bind_release.h index 7829ec54..13869554 100644 --- a/jni_bind_release.h +++ b/jni_bind_release.h @@ -849,16 +849,16 @@ struct Method, std::tuple> // CTAD for Non-overloaded form, no Params. template >> -Method(const char*, - ReturnT) -> Method, std::tuple>>; +Method(const char*, ReturnT) + -> Method, std::tuple>>; // CTAD for Non-overloaded form. template < typename ReturnT, typename ParamsT, typename = std::enable_if_t && !std::is_base_of_v>> -Method(const char*, ReturnT, - ParamsT) -> Method, std::tuple>; +Method(const char*, ReturnT, ParamsT) + -> Method, std::tuple>; // CTAD for Overloaded form. template @@ -1625,6 +1625,8 @@ struct RootObject { static constexpr RootObject kObject{}; static constexpr struct NoClass { + // For compatability reasons, this must be defined (not default) because some + // compilers will complain about defaulted constructors being deleted. constexpr NoClass() {} const char* name_ = "__JNI_BIND__NO_CLASS__"; @@ -1632,11 +1634,15 @@ static constexpr struct NoClass { const Static, std::tuple<>> static_{}; const std::tuple<> methods_{}; const std::tuple<> fields_{}; - - constexpr bool operator==(const NoClass&) const { return true; } - constexpr bool operator!=(const NoClass&) const { return true; } } kNoClassSpecified; +constexpr bool operator==(const NoClass& lhs, const NoClass& rhs) { + return true; +} +constexpr bool operator!=(const NoClass& lhs, const NoClass& rhs) { + return false; +} + } // namespace jni // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" @@ -1678,6 +1684,10 @@ Extends(T) -> Extends; // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include +#include +#include + namespace jni { struct ConstructorBase {}; @@ -1706,15 +1716,15 @@ class Constructor : public ConstructorBase { constexpr explicit Constructor(Args...) {} }; -template -Constructor(ParamsRaw...) -> Constructor; - template constexpr bool operator==(const Constructor& lhs, const Constructor& rhs) { return lhs.params_ == rhs.params_; } +template +Constructor(ParamsRaw...) -> Constructor; + //============================================================================== // Represents a constructor used at runtime and has index data about where it // exists in the static class definition which is embedded on the caller's @@ -2022,27 +2032,45 @@ struct Class, static_(statik), methods_(methods...), fields_(fields...) {} +}; - //////////////////////////////////////////////////////////////////////////////// - // Equality operators. - //////////////////////////////////////////////////////////////////////////////// - template - constexpr bool operator==( - const Class, - std::tuple, - std::tuple>>, - std::tuple, std::tuple>& rhs) const { - // Don't compare the other parameters so classes can be used as parameters - // or return values before the class itself is defined. - return std::string_view(name_) == std::string_view(rhs.name_); - } +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +constexpr bool operator==( + const Class, + std::tuple, + std::tuple>>, + std::tuple, std::tuple>& lhs, + const Class, + std::tuple, + std::tuple>>, + std::tuple, std::tuple>& rhs) { + // Don't compare the other parameters so classes can be used as parameters + // or return values before the class itself is defined. + return std::string_view(lhs.name_) == std::string_view(rhs.name_); +} - constexpr bool operator==(const NoClass&) const { return false; } - constexpr bool operator!=(const NoClass&) const { return false; } -}; +template +constexpr bool operator==(const Class& lhs, const NoClass&) { + return false; +} + +template +constexpr bool operator!=(const Class& lhs, const NoClass&) { + return true; +} +//////////////////////////////////////////////////////////////////////////////// +// CTAD. +//////////////////////////////////////////////////////////////////////////////// template Class(const char*, Params...) -> Class struct Rank {}; +struct ArrayBase {}; + //////////////////////////////////////////////////////////////////////////////// // Array Non-Object Implementation. //////////////////////////////////////////////////////////////////////////////// template -struct ArrayNonObjectTypeImpl { +struct ArrayNonObjectTypeImpl : ArrayBase { RawType raw_; constexpr ArrayNonObjectTypeImpl(RawType raw) : raw_(raw) {} @@ -2092,21 +2122,48 @@ struct ArrayNonObjectTypeImpl { "JNI Error: Invalid array declaration, use Array { type{}, " "Rank{} }."); } +}; - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { - return (raw_ == rhs.raw_); - } - return false; - } +template +struct ArrayComparisonHelper {}; - template - constexpr bool operator!=(const Array& rhs) const { - return !(*this == rhs); - } +template +struct ArrayComparisonHelper>> { + using type = RawType; +}; + +template +struct ArrayComparisonHelper> { + using type = RawType; +}; + +template +using ArrayComparisonHelper_t = typename ArrayComparisonHelper::type; + +template +static constexpr bool IsArrayComparable() { + return std::is_base_of_v && std::is_base_of_v; }; +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +constexpr std::enable_if_t(), bool> operator==( + const T1& lhs, const T2& rhs) { + if constexpr (std::is_same_v, + ArrayComparisonHelper_t>) { + return (lhs.raw_ == rhs.raw_); + } + return false; +} + +template +constexpr std::enable_if_t(), bool> operator!=( + const T1& lhs, const T2& rhs) { + return !(lhs == rhs); +} + // Primitive array implementaiton. template struct ArrayImpl : public ArrayNonObjectTypeImpl, @@ -2119,27 +2176,32 @@ struct ArrayImpl : public ArrayNonObjectTypeImpl, // Array Object Implementation. //////////////////////////////////////////////////////////////////////////////// template -struct ArrayImpl : public ArrayTag { +struct ArrayImpl : public ArrayTag, + ArrayBase { RawType raw_; constexpr ArrayImpl(RawType raw) : raw_(raw) {} template constexpr ArrayImpl(RawType raw, Rank) : raw_(raw) {} +}; - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { - return (raw_ == rhs.raw_); - } - return false; +template +constexpr bool operator==(const ArrayImpl& lhs, + const ArrayImpl& rhs) { + if constexpr (std::is_same_v) { + return (lhs.raw_ == rhs.raw_); } + return false; +} - template - constexpr bool operator!=(const Array& rhs) const { - return !(*this == rhs); - } -}; +template +constexpr bool operator!=(const ArrayImpl& lhs, + const Array& rhs) { + return !(lhs == rhs); +} // This type correlates to those used in declarations, // e.g. Field { Array { Array { jint {} } } }. @@ -2303,98 +2365,6 @@ using Increment_t = typename Increment::template type; } // namespace jni::metaprogramming -namespace jni { - -enum class LifecycleType { - LOCAL, - GLOBAL, - // WEAK, // not implemented yet. -}; - -template -struct LifecycleHelper; - -// Shared implementation for local jobjects (jobject, jstring). -template -struct LifecycleLocalBase { - static inline void Delete(Span object) { - Trace(metaprogramming::LambdaToStr(STR("DeleteLocalRef")), object); - -#ifdef DRY_RUN -#else - JniEnv::GetEnv()->DeleteLocalRef(object); -#endif // DRY_RUN - } - - static inline Span NewReference(Span object) { - Trace(metaprogramming::LambdaToStr(STR("NewLocalRef")), object); - -#ifdef DRY_RUN - return Fake(); -#else - return static_cast(JniEnv::GetEnv()->NewLocalRef(object)); -#endif // DRY_RUN - } -}; - -template -struct LifecycleHelper - : public LifecycleLocalBase { - using Base = LifecycleLocalBase; - using Base::Base; -}; - -// Shared implementation for global jobjects (jobject, jstring). -template -struct LifecycleGlobalBase { - static inline Span Promote(Span object) { - Trace(metaprogramming::LambdaToStr(STR("NewGlobalRef")), object); - -#ifdef DRY_RUN - jobject ret = Fake(); -#else - jobject ret = JniEnv::GetEnv()->NewGlobalRef(object); -#endif // DRY_RUN - - Trace(metaprogramming::LambdaToStr(STR("DeleteLocalRef")), object); - -#ifdef DRY_RUN -#else - JniEnv::GetEnv()->DeleteLocalRef(object); -#endif // DRY_RUN - - return static_cast(ret); - } - - static inline void Delete(Span object) { - Trace(metaprogramming::LambdaToStr(STR("DeleteGlobalRef")), object); - -#ifdef DRY_RUN -#else - JniEnv::GetEnv()->DeleteGlobalRef(object); -#endif // DRY_RUN - } - - static inline Span NewReference(Span object) { - Trace(metaprogramming::LambdaToStr(STR("NewGlobalRef")), object); - -#ifdef DRY_RUN - return Fake(); -#else - return static_cast(JniEnv::GetEnv()->NewGlobalRef(object)); -#endif // DRY_RUN - } -}; - -template -struct LifecycleHelper - : public LifecycleLocalBase { - using Base = LifecycleGlobalBase; - using Base::Base; -}; - -} // namespace jni - // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" #include @@ -2462,18 +2432,21 @@ class DefaultClassLoader { constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { return kClassNotInLoaderSetIdx; } +}; - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const DefaultClassLoader&) const { return true; } +template +bool constexpr operator==(const DefaultClassLoader& lhs, const T& rhs) { + return false; +} +bool constexpr operator==(const DefaultClassLoader& lhs, + const DefaultClassLoader& rhs) { + return true; +} - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } -}; +template +bool constexpr operator!=(const DefaultClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} // Class loader that cannot supply any classes. This should be the root loader // for most user defined classes. @@ -2495,18 +2468,21 @@ class NullClassLoader { constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { return kClassNotInLoaderSetIdx; } +}; - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const NullClassLoader&) const { return true; } +template +constexpr bool operator==(const NullClassLoader& lhs, const T& rhs) { + return false; +} +constexpr bool operator==(const NullClassLoader& lhs, + const NullClassLoader& rhs) { + return true; +} - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } -}; +template +constexpr bool operator!=(const NullClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} static constexpr NullClassLoader kNullClassLoader; static constexpr DefaultClassLoader kDefaultClassLoader; @@ -2740,101 +2716,93 @@ struct UserDefined; namespace jni { -// jobject. -template <> -struct LifecycleHelper - : public LifecycleLocalBase { - template - static inline jobject Construct(jclass clazz, jmethodID ctor_method, - CtorArgs&&... ctor_args) { - Trace(metaprogramming::LambdaToStr(STR("NewObject")), clazz, ctor_method, - ctor_args...); +enum class LifecycleType { + LOCAL, + GLOBAL, + // WEAK, // not implemented yet. +}; + +template +struct LifecycleHelper; + +// Shared implementation for local jobjects (jobject, jstring). +template +struct LifecycleLocalBase { + static inline void Delete(Span object) { + Trace(metaprogramming::LambdaToStr(STR("DeleteLocalRef")), object); #ifdef DRY_RUN - return Fake(); #else - return JniEnv::GetEnv()->NewObject(clazz, ctor_method, ctor_args...); + JniEnv::GetEnv()->DeleteLocalRef(object); #endif // DRY_RUN } -}; - -template <> -struct LifecycleHelper - : public LifecycleGlobalBase { - template - static inline jobject Construct(jclass clazz, jmethodID ctor_method, - CtorArgs&&... ctor_args) { - using Local = LifecycleHelper; - jobject local_object = Local::Construct( - clazz, ctor_method, std::forward(ctor_args)...); - jobject global_object = Promote(local_object); - Local::Delete(local_object); + static inline Span NewReference(Span object) { + Trace(metaprogramming::LambdaToStr(STR("NewLocalRef")), object); - return global_object; +#ifdef DRY_RUN + return Fake(); +#else + return static_cast(JniEnv::GetEnv()->NewLocalRef(object)); +#endif // DRY_RUN } }; -// jclass. -template <> -struct LifecycleHelper - : public LifecycleLocalBase {}; +template +struct LifecycleHelper + : public LifecycleLocalBase { + using Base = LifecycleLocalBase; + using Base::Base; +}; -template <> -struct LifecycleHelper - : public LifecycleGlobalBase {}; +// Shared implementation for global jobjects (jobject, jstring). +template +struct LifecycleGlobalBase { + static inline Span Promote(Span object) { + Trace(metaprogramming::LambdaToStr(STR("NewGlobalRef")), object); -} // namespace jni +#ifdef DRY_RUN + jobject ret = Fake(); +#else + jobject ret = JniEnv::GetEnv()->NewGlobalRef(object); +#endif // DRY_RUN -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" + Trace(metaprogramming::LambdaToStr(STR("DeleteLocalRef")), object); -#include -#include -#include - -namespace jni { - -template -class Jvm { - public: - const std::tuple class_loaders_; - - constexpr Jvm(ClassLoaderTs... class_loaders) - : class_loaders_(class_loaders...) {} +#ifdef DRY_RUN +#else + JniEnv::GetEnv()->DeleteLocalRef(object); +#endif // DRY_RUN - template - constexpr size_t IdxOfClassLoaderHelper( - std::integer_sequence) const { - return metaprogramming::ModifiedMax( - {((std::get(class_loaders_) == class_loader_v) ? Is : -1)...}); + return static_cast(ret); } - // Returns the index for a given classloader within this set (any given class - // ref is defined by this index). - template - constexpr size_t IdxOfClassLoader() const { - return IdxOfClassLoaderHelper( - std::make_integer_sequence()); - } + static inline void Delete(Span object) { + Trace(metaprogramming::LambdaToStr(STR("DeleteGlobalRef")), object); - template - bool constexpr operator==(const T& rhs) const { - return false; +#ifdef DRY_RUN +#else + JniEnv::GetEnv()->DeleteGlobalRef(object); +#endif // DRY_RUN } - bool constexpr operator==(const Jvm&) const { return true; } - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); + static inline Span NewReference(Span object) { + Trace(metaprogramming::LambdaToStr(STR("NewGlobalRef")), object); + +#ifdef DRY_RUN + return Fake(); +#else + return static_cast(JniEnv::GetEnv()->NewGlobalRef(object)); +#endif // DRY_RUN } }; -template -Jvm(ClassLoaderTs...) -> Jvm; - -// Convenience Jvm definition. -// Compatible with default class loader or specified loaders. -inline constexpr Jvm kDefaultJvm{kDefaultClassLoader}; +template +struct LifecycleHelper + : public LifecycleLocalBase { + using Base = LifecycleGlobalBase; + using Base::Base; +}; } // namespace jni @@ -2858,6 +2826,7 @@ enum class IdType { // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include #include #include #include @@ -2902,20 +2871,6 @@ class ClassLoader : public Object { parent_loader_(parent_loader), supported_classes_(supported_class_set.supported_classes_) {} - bool constexpr operator==( - const ClassLoader& rhs) const { - return (*this).parent_loader_ == rhs.parent_loader_ && - (*this).supported_classes_ == rhs.supported_classes_; - } - template - bool constexpr operator==(const T& rhs) const { - return false; - } - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } - template constexpr std::size_t IdxOfClassHelper( std::integer_sequence) const { @@ -2965,6 +2920,33 @@ class ClassLoader : public Object { } }; +//////////////////////////////////////////////////////////////////////////////// +// Equality operators. +//////////////////////////////////////////////////////////////////////////////// +template +bool constexpr operator==( + const ClassLoader& lhs, + const ClassLoader& rhs) { + return lhs.parent_loader_ == rhs.parent_loader_ && + lhs.supported_classes_ == rhs.supported_classes_; +} + +template +bool constexpr operator==( + const ClassLoader& lhs, const T& rhs) { + return false; +} + +template +bool constexpr operator!=( + const ClassLoader& lhs, const T& rhs) { + return !(lhs == rhs); +} + +//////////////////////////////////////////////////////////////////////////////// +// CTAD. +//////////////////////////////////////////////////////////////////////////////// + // Note: Null is chosen, not default, because LoadedBy requires a syntax like // LoadedBy{ClassLoader{"kClass"}} (using the CTAD loader type below), but // we want to prevent explicit usage of a default loader (as it makes no sense). @@ -2980,6 +2962,9 @@ template ClassLoader(SupportedClassSet) -> ClassLoader; +//////////////////////////////////////////////////////////////////////////////// +// Ancestral lookups. +//////////////////////////////////////////////////////////////////////////////// template constexpr auto& GetAncestor(const T& loader) { if constexpr (I == 0) { @@ -3456,13 +3441,13 @@ class RefBase : public RefBaseBase { typename = std::enable_if_t>> RefBase(RefBase&& rhs) : object_ref_(rhs.Release()) {} + // Releases ownership of the underlying object, further use is undefined. StorageType Release() { StorageType return_value = object_ref_; object_ref_ = nullptr; return return_value; } - explicit operator StorageType() const { return object_ref_; } protected: @@ -3522,6 +3507,8 @@ static constexpr bool IsConvertibleKey_v = // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include + namespace jni { // Use these pass through macros to avoid clang-tidy warnings. @@ -3535,6 +3522,9 @@ namespace jni { // Provide this base tag to UserDefined to enable custom types. struct JniUserDefinedCorpusTag {}; +// ArrayViewHelperBase (shared by all `ArrayView`). +struct ArrayViewHelperBase; + // ArrayView Helper. template struct ArrayViewHelper; @@ -3985,6 +3975,63 @@ constexpr auto StripClassLoaderFromLoadedBy(T val) { } // namespace jni +// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" + +#include +#include +#include + +namespace jni { + +template +class Jvm { + public: + const std::tuple class_loaders_; + + constexpr Jvm(ClassLoaderTs... class_loaders) + : class_loaders_(class_loaders...) {} + + template + constexpr size_t IdxOfClassLoaderHelper( + std::integer_sequence) const { + return metaprogramming::ModifiedMax( + {((std::get(class_loaders_) == class_loader_v) ? Is : -1)...}); + } + + // Returns the index for a given classloader within this set (any given class + // ref is defined by this index). + template + constexpr size_t IdxOfClassLoader() const { + return IdxOfClassLoaderHelper( + std::make_integer_sequence()); + } +}; + +template +constexpr bool operator==(const Jvm& lhs, const T& rhs) { + return false; +} + +template +constexpr bool operator==(const Jvm& lhs, + const Jvm& rhs) { + return true; +} + +template +bool constexpr operator!=(const Jvm& lhs, const T& rhs) { + return !(lhs == rhs); +} + +template +Jvm(ClassLoaderTs...) -> Jvm; + +// Convenience Jvm definition. +// Compatible with default class loader or specified loaders. +inline constexpr Jvm kDefaultJvm{kDefaultClassLoader}; + +} // namespace jni + #include #include @@ -4439,67 +4486,53 @@ struct JniTSelector { } // namespace jni -#include -#include -#include - -namespace jni::metaprogramming { - -template -using T_ = T; - -template -auto TupleFromSize(std::index_sequence) { - return std::tuple...>{}; -} - -// Takes a type and returns a std::tuple of DefaultValues. -template -auto TupleFromSize() { - return TupleFromSize(std::make_index_sequence{}); -} - -template -using TupleFromSize_t = decltype(TupleFromSize()); +namespace jni { -} // namespace jni::metaprogramming +// jobject. +template <> +struct LifecycleHelper + : public LifecycleLocalBase { + template + static inline jobject Construct(jclass clazz, jmethodID ctor_method, + CtorArgs&&... ctor_args) { + Trace(metaprogramming::LambdaToStr(STR("NewObject")), clazz, ctor_method, + ctor_args...); -#include -#include -#include -#include +#ifdef DRY_RUN + return Fake(); +#else + return JniEnv::GetEnv()->NewObject(clazz, ctor_method, ctor_args...); +#endif // DRY_RUN + } +}; -namespace jni::metaprogramming { +template <> +struct LifecycleHelper + : public LifecycleGlobalBase { + template + static inline jobject Construct(jclass clazz, jmethodID ctor_method, + CtorArgs&&... ctor_args) { + using Local = LifecycleHelper; -// Returns a null pointer of the type of the two input tuples interleaved. -template -auto Interleave(std::integer_sequence) - -> decltype(std::tuple_cat( - std::make_tuple(std::get(std::declval()), - std::get(std::declval()))...))* { - // This interleave is for *types only*, all values within the tuples are - // completely incidental. In the event there is no default constructor, it - // won't be possible to return a value, so, instead, return a pointer (which - // won't be used) and infer the type by stripping the pointer. - return nullptr; -} + jobject local_object = Local::Construct( + clazz, ctor_method, std::forward(ctor_args)...); + jobject global_object = Promote(local_object); + Local::Delete(local_object); -template -auto Interleave() { - return Interleave( - std::make_index_sequence::value>()); -} + return global_object; + } +}; -template -struct Interleaved; +// jclass. +template <> +struct LifecycleHelper + : public LifecycleLocalBase {}; -template -struct Interleaved, std::tuple> { - using type = std::remove_pointer_t< - decltype(Interleave, std::tuple>())>; -}; +template <> +struct LifecycleHelper + : public LifecycleGlobalBase {}; -} // namespace jni::metaprogramming +} // namespace jni #include #include @@ -5027,792 +5060,417 @@ struct InvokeHelper 1), jobject>, kRank, false> { } // namespace jni -#include -#include +// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" + +#include +#include namespace jni { -template -struct FieldHelper { - static Raw GetValue(jobject object_ref, jfieldID field_ref_); +// Used as shared storage of lists for IDs like jclass, jMethod, etc. +// Only applicable for Jvms not fully specified (i.e. default classloader). +// See JvmRef::~JvmRef. +template +static std::vector*>& DefaultRefs() { + static auto* ret_val = + new std::vector*>{}; + return *ret_val; +} - static void SetValue(jobject object_ref, jfieldID field_ref_, Raw&& value); +// Provides a static inline `DoubleLockedValue` val against a `UniqueID`. +template +struct StaticDoubleLock { + static inline metaprogramming::DoubleLockedValue val; }; -//////////////////////////////////////////////////////////////////////////////// -// Rank 0: Primitive types (e.g. int). -//////////////////////////////////////////////////////////////////////////////// -template <> -struct FieldHelper { - static inline jboolean GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetBooleanValue")), object_ref, - field_ref_); +// Takes a GetLambda and only invokes it for the first time on equal values of +// `SignatureLambda`. This is useful for putting `const char*` into type IDs. +template +struct RefStorage { + // Return of `GetLambda`. + using ReturnT = decltype(std::declval()(nullptr)); -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetBooleanField(object_ref, field_ref_); -#endif // DRY_RUN - } + // Compile-time unique ID. + static constexpr auto kSignature = []() { + return SignatureLambda::TypeName().data(); + }; + using Signature = metaprogramming::LambdaStringToType; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jboolean&& value) { - Trace(metaprogramming::LambdaToStr(STR("GetBooleanValue")), object_ref, - field_ref_); + // Common ID-wide double locked value. + using Storage = StaticDoubleLock; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetBooleanField(object_ref, field_ref_, value); -#endif // DRY_RUN + // Retrieves the guarded value, possibly invoking the expensive lambda. + static ReturnT Get(GetLambda lambda) { + return StaticDoubleLock::val.LoadAndMaybeInit( + std::bind(lambda, &Storage::val)); } }; -template <> -struct FieldHelper { - static inline jbyte GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetByteValue")), object_ref, - field_ref_); +} // namespace jni -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetByteField(object_ref, field_ref_); -#endif // DRY_RUN - } +// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jbyte&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetByteValue")), object_ref, - field_ref_); +#include +#include +#include +#include -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetByteField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; +namespace jni { -template <> -struct FieldHelper { - static inline jchar GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetCharValue")), object_ref, - field_ref_); +template +struct ArrayHelper; -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetCharField(object_ref, field_ref_); -#endif // DRY_RUN - } +template +class LocalArray; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jchar&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetCharValue")), object_ref, - field_ref_); +template +struct Proxy>> + : public ProxyBase { + // Non-array primitive type (e.g. jintArray => jint). + using CDecl = ArrayToRegularTypeMap_t; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetCharField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; + // Primitive Array Types (e.g. if JArrayType is jintarray and T is too). + template + struct Helper { + static constexpr bool val = + (std::is_same_v && ParamSelection::kRank == 1) || + (std::is_same_v && ParamSelection::kRank >= 2); + }; -template <> -struct FieldHelper { - static inline jshort GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetShortValue")), object_ref, - field_ref_); + // LocalArray. + template + struct Helper> { + static constexpr auto param_copy = FullArrayStripV(ParamSelection::Val()); -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetShortField(object_ref, field_ref_); -#endif // DRY_RUN - } + static constexpr bool val = + (kRank == ParamSelection::kRank) && + (std::is_same_v || + (std::is_same_v && + ParamSelection::kRank >= 2) || + (std::string_view{class_v_.name_} == NameOrNothing_v)); + }; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jshort&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetShortValue")), object_ref, - field_ref_); + template + static constexpr bool kViable = Helper::val; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetShortField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; + using AsDecl = std::tuple>; + using AsArg = + std::tuple, ArrayTag>; -template <> -struct FieldHelper { - static inline jint GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetIntValue")), object_ref, - field_ref_); + template + using AsReturn = typename ArrayHelper::AsReturn; -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetIntField(object_ref, field_ref_); -#endif // DRY_RUN - } + static JArrayType ProxyAsArg(JArrayType arr) { return arr; }; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jint&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetIntValue")), object_ref, - field_ref_); + template + static JArrayType ProxyAsArg(const T& t) { + return JArrayType{t}; + }; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetIntField(object_ref, field_ref_, value); -#endif // DRY_RUN - } + template , T>>> + static JArrayType ProxyAsArg(T&& t) { + return t.Release(); + }; }; -template <> -struct FieldHelper { - static inline jlong GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetLongField")), object_ref, - field_ref_); - -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetLongField(object_ref, field_ref_); -#endif // DRY_RUN - } +// This must be defined outside of Proxy so implicit definition doesn't occur. +template +struct ArrayHelper { + template + struct Helper { + static constexpr auto val = FullArrayStripV(t.raw_); - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jlong&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetLongField")), object_ref, - field_ref_); + using StrippedCDecl = CDecl_t>; + using ConvertedCDecl = RegularToArrayTypeMap_t; + }; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetLongField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; + static constexpr auto kVal{IdT::Materialize()}; -template <> -struct FieldHelper { - static inline jfloat GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetFloatField")), object_ref, - field_ref_); + static constexpr auto LocalArrayBuildFromArray() { + using RawT = typename IdT::RawMaterializeT; + constexpr std::size_t kRank = IdT::kMaterializedRank; -#ifdef DRY_RUN - return 123.f; -#else - return jni::JniEnv::GetEnv()->GetFloatField(object_ref, field_ref_); -#endif // DRY_RUN + if constexpr (!std::is_same_v, jobject>) { + return LocalArray{1}; + } else { + return LocalArray::val, kDefaultClassLoader, + kDefaultJvm>{jobjectArray{nullptr}}; + } } - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jfloat&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetFloatField")), object_ref, - field_ref_); + using StrippedCDecl = typename Helper::StrippedCDecl; + using ConvertedCDecl = typename Helper::ConvertedCDecl; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetFloatField(object_ref, field_ref_, value); -#endif // DRY_RUN - } + using AsReturn = decltype(LocalArrayBuildFromArray()); }; -template <> -struct FieldHelper { - static inline jdouble GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetDoubleField")), object_ref, - field_ref_); +} // namespace jni -#ifdef DRY_RUN - return 123.; -#else - return jni::JniEnv::GetEnv()->GetDoubleField(object_ref, field_ref_); -#endif // DRY_RUN - } +// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jdouble&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetDoubleField")), object_ref, - field_ref_); +#include +#include +#include -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetDoubleField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; +namespace jni { -template <> -struct FieldHelper { - static inline jobject GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetObjectField")), object_ref, - field_ref_); +template +struct ProxyHelper; -#ifdef DRY_RUN - return Fake(); -#else - return jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_); -#endif // DRY_RUN - } +// Proxy is a metafunction that gives useful conversions from +// types and forwards to a corresponding type that's viable as input. +// +// Note, given the context, different types present differently. E.g. a |jint| +// is always a jint, but a |jobject| is declared as a |jni::Class|, passed as a +// |jni::RefBase&| and then converted to a |jobject| to cross the C API. +// +// |Proxy_t| will select the correct proxy for any of the above types. To be +// specific, |Proxy_t| of any type in |Arg| or |AsDecl| will return +// the parent Proxy. +// +// Each proxy exports aliases for a given |CDecl|. +// |Index|: A uniquely identifying Key for proxy lookup. This is usually the +// CDecl (e.g. jint => jint), but rich types may differ (Object =>jobject). +// |CDecl|: This is both the unique ID for a given proxy, as well as the +// distinct type (of which there is only one) that is usable when invoking a +// JNI call through the C API (e.g. |jint|, |jobject|). +// |AsArg|: All valid passable types. +// |AsDecl|: The type to be used in a function declaration, either as +// return or as a declared argument. If is templated by |class_v| and +// |class_loader_v| which can allow for additional decoration. +template +struct Proxy : public ProxyBase {}; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jobject&& new_value) { - Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, - field_ref_); +template +struct Proxy>> + : public ProxyBase {}; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); -#endif // DRY_RUN - } +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; + + template + static constexpr bool kViable = IsConvertibleKey::template value || + IsConvertibleKey::template value; }; -template <> -struct FieldHelper { - static inline jstring GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetObjectField")), object_ref, - field_ref_); +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; -#ifdef DRY_RUN - return Fake(); -#else - return reinterpret_cast( - jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_)); -#endif // DRY_RUN - } + template + static constexpr bool kViable = + IsConvertibleKey::template value || + IsConvertibleKey::template value; +}; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jstring&& new_value) { - Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, - field_ref_); +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); -#endif // DRY_RUN + template + static constexpr bool kViable = IsConvertibleKey::template value || + IsConvertibleKey::template value; + + static jlong ProxyAsArg(jlong val) { return val; } + + // jlong is a smaller type on ARM than x86. + // When jlong is not equivalent, we upcast to the wider type. + template && + !std::is_same_v>> + static jlong ProxyAsArg(T val) { + return jlong{val}; } }; //////////////////////////////////////////////////////////////////////////////// -// Rank 1: Single dimension arrays (e.g. int[]). +// Object Proxy Definitions. //////////////////////////////////////////////////////////////////////////////// -template -struct BaseFieldArrayHelper { - static inline ArrayType GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetObjectField, Rank 1")), - object_ref, field_ref_); - -#ifdef DRY_RUN - return Fake(); -#else - return static_cast( - jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_)); -#endif // DRY_RUN - } +template +struct Proxy>> + : public ProxyBase { + using AsDecl = std::tuple; + using AsArg = std::tuple, LoaderTag>; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, ArrayType&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, - field_ref_); + template + struct ContextualViabilityHelper { + // TODO(b/143908983): This is overly permissive, see method_selection_test. + static constexpr bool kViable = std::is_same_v; + }; -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; + // Old "LocalObject" form. + template class Container, + const auto& class_v, const auto& class_loader_v, const auto& jvm_v> + struct ContextualViabilityHelper> { + static constexpr bool kViable = + std::string_view{class_v.name_} == std::string_view{IdT::Val().name_}; + }; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + // New "LocalObject" form. + template + struct ContextualViabilityHelper< + IdT, Scoped, jobject>> { + static constexpr bool kViable = + std::string_view{class_v.name_} == std::string_view{IdT::Val().name_}; + }; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + template + static constexpr bool kViable = ContextualViabilityHelper::kViable; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + template + struct Helper { + static constexpr auto kClass{Id::Val()}; + static constexpr auto kClassLoader{Id::JniT::GetClassLoader()}; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + // TODO(b/174272629): Class loaders should also be enforced. + using type = LocalObject; + }; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + template + using AsReturn = typename Helper::type; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + static jobject ProxyAsArg(jobject obj) { return obj; }; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + // Applies for both local and global. + template + static jobject ProxyAsArg(T& t) { + return jobject{t}; + }; -template -struct FieldHelper, kRank, false, void> - : BaseFieldArrayHelper {}; + // Applies for both local and global. + template + static jobject ProxyAsArg(T&& t) { + return t.Release(); + }; +}; //////////////////////////////////////////////////////////////////////////////// -// Rank 1: jobjects & jstrings. -// Rank 2+: Multi-dimension arrays (e.g. int[][], int[][][]). +// Self Proxy Definitions. //////////////////////////////////////////////////////////////////////////////// -template -struct FieldHelper< - T, kRank, false, - std::enable_if_t<(std::is_same_v || - std::is_same_v || (kRank > 1))>> { - static inline jobjectArray GetValue(const jobject object_ref, - const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetObjectField, Rank >1")), - object_ref, field_ref_); +template +struct Proxy>> + : public ProxyBase { + using AsDecl = std::tuple; + using AsArg = std::tuple; -#ifdef DRY_RUN - return Fake(); -#else - return static_cast( - jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_)); -#endif // DRY_RUN - } + template + using SelfIdT_t = typename IdT::template ChangeIdType; - static inline void SetValue(const jobject object_ref, - const jfieldID field_ref_, jobjectArray&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetObjectField, Rank >1")), - object_ref, field_ref_); - -#ifdef DRY_RUN -#else - jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, value); -#endif // DRY_RUN - } -}; - -} // namespace jni - -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - -#include -#include - -namespace jni { - -// Used as shared storage of lists for IDs like jclass, jMethod, etc. -// Only applicable for Jvms not fully specified (i.e. default classloader). -// See JvmRef::~JvmRef. -template -static std::vector*>& DefaultRefs() { - static auto* ret_val = - new std::vector*>{}; - return *ret_val; -} - -// Provides a static inline `DoubleLockedValue` val against a `UniqueID`. -template -struct StaticDoubleLock { - static inline metaprogramming::DoubleLockedValue val; -}; - -// Takes a GetLambda and only invokes it for the first time on equal values of -// `SignatureLambda`. This is useful for putting `const char*` into type IDs. -template -struct RefStorage { - // Return of `GetLambda`. - using ReturnT = decltype(std::declval()(nullptr)); - - // Compile-time unique ID. - static constexpr auto kSignature = []() { - return SignatureLambda::TypeName().data(); - }; - using Signature = metaprogramming::LambdaStringToType; - - // Common ID-wide double locked value. - using Storage = StaticDoubleLock; - - // Retrieves the guarded value, possibly invoking the expensive lambda. - static ReturnT Get(GetLambda lambda) { - return StaticDoubleLock::val.LoadAndMaybeInit( - std::bind(lambda, &Storage::val)); - } -}; - -} // namespace jni - -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - -#include -#include -#include -#include - -namespace jni { - -template -struct ArrayHelper; - -template -class LocalArray; - -template -struct Proxy>> - : public ProxyBase { - // Non-array primitive type (e.g. jintArray => jint). - using CDecl = ArrayToRegularTypeMap_t; - - // Primitive Array Types (e.g. if JArrayType is jintarray and T is too). - template + template struct Helper { - static constexpr bool val = - (std::is_same_v && ParamSelection::kRank == 1) || - (std::is_same_v && ParamSelection::kRank >= 2); - }; - - // LocalArray. - template - struct Helper> { - static constexpr auto param_copy = FullArrayStripV(ParamSelection::Val()); + static constexpr auto kClass{Id::Val()}; + static constexpr auto kClassLoader{Id::JniT::GetClassLoader()}; - static constexpr bool val = - (kRank == ParamSelection::kRank) && - (std::is_same_v || - (std::is_same_v && - ParamSelection::kRank >= 2) || - (std::string_view{class_v_.name_} == NameOrNothing_v)); + // TODO(b/174272629): Class loaders should also be enforced. + using type = LocalObject; }; - template - static constexpr bool kViable = Helper::val; - - using AsDecl = std::tuple>; - using AsArg = - std::tuple, ArrayTag>; - template - using AsReturn = typename ArrayHelper::AsReturn; + using AsReturn = typename Helper::type; - static JArrayType ProxyAsArg(JArrayType arr) { return arr; }; + template + static constexpr bool kViable = + Proxy::template kViable, T>; + // Applies for both local and global. template - static JArrayType ProxyAsArg(const T& t) { - return JArrayType{t}; + static jobject ProxyAsArg(T& t) { + return jobject{t}; }; - template , T>>> - static JArrayType ProxyAsArg(T&& t) { + // Applies for both local and global. + template + static jobject ProxyAsArg(T&& t) { return t.Release(); }; }; -// This must be defined outside of Proxy so implicit definition doesn't occur. -template -struct ArrayHelper { - template - struct Helper { - static constexpr auto val = FullArrayStripV(t.raw_); +} // namespace jni - using StrippedCDecl = CDecl_t>; - using ConvertedCDecl = RegularToArrayTypeMap_t; - }; +// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - static constexpr auto kVal{IdT::Materialize()}; +namespace jni { - static constexpr auto LocalArrayBuildFromArray() { - using RawT = typename IdT::RawMaterializeT; - constexpr std::size_t kRank = IdT::kMaterializedRank; +// Adopts a local. +struct AdoptLocal {}; - if constexpr (!std::is_same_v, jobject>) { - return LocalArray{1}; - } else { - return LocalArray::val, kDefaultClassLoader, - kDefaultJvm>{jobjectArray{nullptr}}; - } - } +// Creates an additional reference to the underlying object. +// When used for local, presumes local, for global, presumes global. +struct NewRef {}; - using StrippedCDecl = typename Helper::StrippedCDecl; - using ConvertedCDecl = typename Helper::ConvertedCDecl; +// This tag allows the constructor to promote underlying jobject for you. +struct PromoteToGlobal {}; - using AsReturn = decltype(LocalArrayBuildFromArray()); -}; +// CAUTION: This tag assume the underlying jobject has been pinned as a global. +// This is atypical when solely using JNI Bind, use with caution. +struct AdoptGlobal {}; } // namespace jni // IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" +#include #include -#include -#include namespace jni { -template -struct ProxyHelper; +template +struct Id { + using JniT = JniT_; + static constexpr IdType kIdType = kIdType_; -// Proxy is a metafunction that gives useful conversions from -// types and forwards to a corresponding type that's viable as input. -// -// Note, given the context, different types present differently. E.g. a |jint| -// is always a jint, but a |jobject| is declared as a |jni::Class|, passed as a -// |jni::RefBase&| and then converted to a |jobject| to cross the C API. -// -// |Proxy_t| will select the correct proxy for any of the above types. To be -// specific, |Proxy_t| of any type in |Arg| or |AsDecl| will return -// the parent Proxy. -// -// Each proxy exports aliases for a given |CDecl|. -// |Index|: A uniquely identifying Key for proxy lookup. This is usually the -// CDecl (e.g. jint => jint), but rich types may differ (Object =>jobject). -// |CDecl|: This is both the unique ID for a given proxy, as well as the -// distinct type (of which there is only one) that is usable when invoking a -// JNI call through the C API (e.g. |jint|, |jobject|). -// |AsArg|: All valid passable types. -// |AsDecl|: The type to be used in a function declaration, either as -// return or as a declared argument. If is templated by |class_v| and -// |class_loader_v| which can allow for additional decoration. -template -struct Proxy : public ProxyBase {}; + static constexpr auto Class() { return JniT::GetClass(); } -template -struct Proxy>> - : public ProxyBase {}; + static constexpr std::size_t kIdx = idx; + static constexpr std::size_t kSecondaryIdx = secondary_idx; + static constexpr std::size_t kTertiaryIdx = tertiary_idx; + static constexpr std::size_t kAncestorIdx = ancestry_idx; -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; - using AsDecl = std::tuple; + static constexpr bool kIsConstructor = + (kIdType == IdType::OVERLOAD || kIdType == IdType::OVERLOAD_PARAM || + kIdType == IdType::OVERLOAD_SET) && + (kIdx == kNoIdx); - template - static constexpr bool kViable = IsConvertibleKey::template value || - IsConvertibleKey::template value; -}; + static constexpr bool kIsStatic = kIdType == IdType::STATIC_OVERLOAD_SET || + kIdType == IdType::STATIC_OVERLOAD || + kIdType == IdType::STATIC_OVERLOAD_PARAM || + kIdType == IdType::STATIC_FIELD; -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; - using AsDecl = std::tuple; + template + using ChangeIdType = + Id; - template - static constexpr bool kViable = - IsConvertibleKey::template value || - IsConvertibleKey::template value; -}; - -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; - using AsDecl = std::tuple; - - template - static constexpr bool kViable = IsConvertibleKey::template value || - IsConvertibleKey::template value; - - static jlong ProxyAsArg(jlong val) { return val; } - - // jlong is a smaller type on ARM than x86. - // When jlong is not equivalent, we upcast to the wider type. - template && - !std::is_same_v>> - static jlong ProxyAsArg(T val) { - return jlong{val}; - } -}; - -//////////////////////////////////////////////////////////////////////////////// -// Object Proxy Definitions. -//////////////////////////////////////////////////////////////////////////////// -template -struct Proxy>> - : public ProxyBase { - using AsDecl = std::tuple; - using AsArg = std::tuple, LoaderTag>; - - template - struct ContextualViabilityHelper { - // TODO(b/143908983): This is overly permissive, see method_selection_test. - static constexpr bool kViable = std::is_same_v; - }; - - // Old "LocalObject" form. - template class Container, - const auto& class_v, const auto& class_loader_v, const auto& jvm_v> - struct ContextualViabilityHelper> { - static constexpr bool kViable = - std::string_view{class_v.name_} == std::string_view{IdT::Val().name_}; - }; - - // New "LocalObject" form. - template - struct ContextualViabilityHelper< - IdT, Scoped, jobject>> { - static constexpr bool kViable = - std::string_view{class_v.name_} == std::string_view{IdT::Val().name_}; - }; - - template - static constexpr bool kViable = ContextualViabilityHelper::kViable; - - template - struct Helper { - static constexpr auto kClass{Id::Val()}; - static constexpr auto kClassLoader{Id::JniT::GetClassLoader()}; - - // TODO(b/174272629): Class loaders should also be enforced. - using type = LocalObject; - }; - - template - using AsReturn = typename Helper::type; - - static jobject ProxyAsArg(jobject obj) { return obj; }; - - // Applies for both local and global. - template - static jobject ProxyAsArg(T& t) { - return jobject{t}; - }; - - // Applies for both local and global. - template - static jobject ProxyAsArg(T&& t) { - return t.Release(); - }; -}; - -//////////////////////////////////////////////////////////////////////////////// -// Self Proxy Definitions. -//////////////////////////////////////////////////////////////////////////////// -template -struct Proxy>> - : public ProxyBase { - using AsDecl = std::tuple; - using AsArg = std::tuple; - - template - using SelfIdT_t = typename IdT::template ChangeIdType; - - template - struct Helper { - static constexpr auto kClass{Id::Val()}; - static constexpr auto kClassLoader{Id::JniT::GetClassLoader()}; - - // TODO(b/174272629): Class loaders should also be enforced. - using type = LocalObject; - }; - - template - using AsReturn = typename Helper::type; - - template - static constexpr bool kViable = - Proxy::template kViable, T>; - - // Applies for both local and global. - template - static jobject ProxyAsArg(T& t) { - return jobject{t}; - }; - - // Applies for both local and global. - template - static jobject ProxyAsArg(T&& t) { - return t.Release(); - }; -}; - -} // namespace jni - -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - -namespace jni { - -// Adopts a local. -struct AdoptLocal {}; - -// Creates an additional reference to the underlying object. -// When used for local, presumes local, for global, presumes global. -struct NewRef {}; - -// This tag allows the constructor to promote underlying jobject for you. -struct PromoteToGlobal {}; - -// CAUTION: This tag assume the underlying jobject has been pinned as a global. -// This is atypical when solely using JNI Bind, use with caution. -struct AdoptGlobal {}; - -} // namespace jni - -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - -#include -#include - -namespace jni { - -template -struct Id { - using JniT = JniT_; - static constexpr IdType kIdType = kIdType_; - - static constexpr auto Class() { return JniT::GetClass(); } - - static constexpr std::size_t kIdx = idx; - static constexpr std::size_t kSecondaryIdx = secondary_idx; - static constexpr std::size_t kTertiaryIdx = tertiary_idx; - static constexpr std::size_t kAncestorIdx = ancestry_idx; - - static constexpr bool kIsConstructor = - (kIdType == IdType::OVERLOAD || kIdType == IdType::OVERLOAD_PARAM || - kIdType == IdType::OVERLOAD_SET) && - (kIdx == kNoIdx); - - static constexpr bool kIsStatic = kIdType == IdType::STATIC_OVERLOAD_SET || - kIdType == IdType::STATIC_OVERLOAD || - kIdType == IdType::STATIC_OVERLOAD_PARAM || - kIdType == IdType::STATIC_FIELD; - - template - using ChangeIdType = - Id; - - template - using ChangeIdx = Id; + template + using ChangeIdx = Id; static constexpr auto ValWhenAncestryIs0() { if constexpr (kIdType == IdType::CLASS) { @@ -6064,136 +5722,31 @@ static constexpr bool UnfurlDisjunction_v = } // namespace jni::metaprogramming #include -#include #include -#include #include namespace jni::metaprogramming { -template -class QueryableMapBase {}; +template +using T_ = T; -// This is an interface that can be inherited from to expose an -// operator["name"]. It provides compile time string index lookup with no macros -// although it is dependent on a clang extension. -// -// To use this API, inherit from this class using template types as follows: -// -// |CrtpBase|: The name of the class inheriting from the map. This class -// will inherit an operator[]. It must implement this exact signature: -// -// template -// auto QueryableMapCall(const char* key); -// -// |tup_container_v| is a static instance of an object whose |nameable_member| -// contains a public field called name_. It might seem strange not to -// directly pass a const auto&, however, this prevents accessing subobjects. -// -// The motivation for using inheritance as opposed to a simple member is that -// the the const char cannot be propagated without losing its constexpr-ness, -// and so the clang extension can no longer restrict function candidates. -template -class QueryableMap - : public QueryableMapBase> {}; +template +auto TupleFromSize(std::index_sequence) { + return std::tuple...>{}; +} -template ::*nameable_member> -using QueryableMap_t = - QueryableMap>, - std::decay_t, nameable_member>; +// Takes a type and returns a std::tuple of DefaultValues. +template +auto TupleFromSize() { + return TupleFromSize(std::make_index_sequence{}); +} -template -class QueryableMapEntry; +template +using TupleFromSize_t = decltype(TupleFromSize()); -template -class QueryableMapBase> - : public QueryableMapEntry... { - public: - using QueryableMapEntry::operator[]...; +} // namespace jni::metaprogramming - using QueryableMapEntry::Contains...; - - // Will select subclass specialisations if present. - constexpr bool Contains(const char* key) { return false; } -}; - -template -class QueryableMapEntry { - public: -#if __clang__ - // This function blurs the distinction between type and value space. The - // clang extension allows the key to be wrapped in a constexpr way. This - // allows for string to string comparison based on the static value the class - // is templated by. - // - // The reason the TypeMap interface requires inheritance as opposed to simply - // holding an instance of this map (like you would with a regular hash map) is - // the constexpr-ness of the string can't be propagated. This essentially - // means you get one shot at defining the function. - constexpr auto operator[](const char* key) __attribute__(( - enable_if(std::string_view(key) == - std::get(tup_container_v.*nameable_member).name_, - ""))) { - static_assert(std::is_base_of_v, - "You must derive from the invocable map."); - - return (*static_cast(this)).template QueryableMapCall(key); - } - - constexpr bool Contains(const char* key) __attribute__(( - enable_if(std::string_view(key) == - std::get(tup_container_v.*nameable_member).name_, - ""))) { - return true; - } -#else - static_assert(false, - "This container requires clang for compile time strings."); -#endif -}; - -} // namespace jni::metaprogramming - -#include -#include - -namespace jni::metaprogramming { - -template -struct OptionalTup {}; - -// Takes a _tuple_ of types and returns a tuple with the same types except -// wrapped in std::optional. -template -struct OptionalTup> { - using type = std::tuple...>; -}; - -template -using OptionalTup_t = typename OptionalTup::type; - -} // namespace jni::metaprogramming - -#include +#include namespace jni::metaprogramming { @@ -6237,6 +5790,43 @@ static constexpr auto Min_v = Min_t::val; } // namespace jni::metaprogramming +#include +#include +#include +#include + +namespace jni::metaprogramming { + +// Returns a null pointer of the type of the two input tuples interleaved. +template +auto Interleave(std::integer_sequence) + -> decltype(std::tuple_cat( + std::make_tuple(std::get(std::declval()), + std::get(std::declval()))...))* { + // This interleave is for *types only*, all values within the tuples are + // completely incidental. In the event there is no default constructor, it + // won't be possible to return a value, so, instead, return a pointer (which + // won't be used) and infer the type by stripping the pointer. + return nullptr; +} + +template +auto Interleave() { + return Interleave( + std::make_index_sequence::value>()); +} + +template +struct Interleaved; + +template +struct Interleaved, std::tuple> { + using type = std::remove_pointer_t< + decltype(Interleave, std::tuple>())>; +}; + +} // namespace jni::metaprogramming + #include namespace jni::metaprogramming { @@ -6267,273 +5857,287 @@ using Call_t = typename Call::type; namespace jni { +template +struct FieldHelper { + static Raw GetValue(jobject object_ref, jfieldID field_ref_); + + static void SetValue(jobject object_ref, jfieldID field_ref_, Raw&& value); +}; + //////////////////////////////////////////////////////////////////////////////// -// Rank 0: Static primitive types (e.g. int). +// Rank 0: Primitive types (e.g. int). //////////////////////////////////////////////////////////////////////////////// template <> -struct FieldHelper { - static inline jboolean GetValue(const jclass clazz, +struct FieldHelper { + static inline jboolean GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticBooleanField")), clazz, + Trace(metaprogramming::LambdaToStr(STR("GetBooleanValue")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticBooleanField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetBooleanField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jboolean&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticBooleanField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jboolean&& value) { + Trace(metaprogramming::LambdaToStr(STR("GetBooleanValue")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticBooleanField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetBooleanField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jbyte GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticByteField")), clazz, +struct FieldHelper { + static inline jbyte GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetByteValue")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticByteField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetByteField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jbyte&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticByteField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jbyte&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetByteValue")), object_ref, + field_ref_); #ifdef DRY_RUN #else - return jni::JniEnv::GetEnv()->SetStaticByteField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetByteField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jchar GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticCharField")), clazz, +struct FieldHelper { + static inline jchar GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetCharValue")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticCharField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetCharField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jchar&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticCharField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jchar&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetCharValue")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticCharField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetCharField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jshort GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticShortField")), clazz, +struct FieldHelper { + static inline jshort GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetShortValue")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticShortField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetShortField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jshort&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticShortField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jshort&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetShortValue")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticShortField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetShortField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jint GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticIntField")), clazz, +struct FieldHelper { + static inline jint GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetIntValue")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticIntField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetIntField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jint&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticIntField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jint&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetIntValue")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticIntField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetIntField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jlong GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticLongField")), clazz, +struct FieldHelper { + static inline jlong GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetLongField")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticLongField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetLongField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jlong&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticLongField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jlong&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetLongField")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticLongField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetLongField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jfloat GetValue(const jclass clazz, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticFloatField")), clazz, +struct FieldHelper { + static inline jfloat GetValue(const jobject object_ref, + const jfieldID field_ref_) { + Trace(metaprogramming::LambdaToStr(STR("GetFloatField")), object_ref, field_ref_); #ifdef DRY_RUN return 123.f; #else - return jni::JniEnv::GetEnv()->GetStaticFloatField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetFloatField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jfloat&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticFloatField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jfloat&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetFloatField")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticFloatField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetFloatField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jdouble GetValue(const jclass clazz, +struct FieldHelper { + static inline jdouble GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticDoubleField")), clazz, + Trace(metaprogramming::LambdaToStr(STR("GetDoubleField")), object_ref, field_ref_); #ifdef DRY_RUN return 123.; #else - return jni::JniEnv::GetEnv()->GetStaticDoubleField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetDoubleField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jdouble&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticDoubleField")), clazz, - field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jdouble&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetDoubleField")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticDoubleField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetDoubleField(object_ref, field_ref_, value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jobject GetValue(const jclass clazz, +struct FieldHelper { + static inline jobject GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticObjectField")), clazz, + Trace(metaprogramming::LambdaToStr(STR("GetObjectField")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else - return jni::JniEnv::GetEnv()->GetStaticObjectField(clazz, field_ref_); + return jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jobject&& new_value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticObjectField")), clazz, - field_ref_, new_value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jobject&& new_value) { + Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticObjectField(clazz, field_ref_, new_value); + jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); #endif // DRY_RUN } }; template <> -struct FieldHelper { - static inline jstring GetValue(const jclass clazz, +struct FieldHelper { + static inline jstring GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticObjectField")), clazz, + Trace(metaprogramming::LambdaToStr(STR("GetObjectField")), object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else return reinterpret_cast( - jni::JniEnv::GetEnv()->GetStaticObjectField(clazz, field_ref_)); + jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_)); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jstring&& new_value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticObjectField")), clazz, - field_ref_, new_value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jstring&& new_value) { + Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, + field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticObjectField(clazz, field_ref_, new_value); + jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); #endif // DRY_RUN } }; //////////////////////////////////////////////////////////////////////////////// -// Rank 1: Static single dimension arrays (e.g. int[]). +// Rank 1: Single dimension arrays (e.g. int[]). //////////////////////////////////////////////////////////////////////////////// template -struct StaticBaseFieldArrayHelper { +struct BaseFieldArrayHelper { static inline ArrayType GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetObjectField")), object_ref, - field_ref_); + Trace(metaprogramming::LambdaToStr(STR("GetObjectField, Rank 1")), + object_ref, field_ref_); #ifdef DRY_RUN return Fake(); @@ -6546,7 +6150,7 @@ struct StaticBaseFieldArrayHelper { static inline void SetValue(const jobject object_ref, const jfieldID field_ref_, ArrayType&& value) { Trace(metaprogramming::LambdaToStr(STR("SetObjectField")), object_ref, - field_ref_, value); + field_ref_); #ifdef DRY_RUN #else @@ -6556,66 +6160,67 @@ struct StaticBaseFieldArrayHelper { }; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; template -struct FieldHelper, kRank, true, void> - : StaticBaseFieldArrayHelper {}; +struct FieldHelper, kRank, false, void> + : BaseFieldArrayHelper {}; //////////////////////////////////////////////////////////////////////////////// -// Rank 1: Static jobjects. -// Rank 2+: Static multi-dimension arrays (e.g. int[][], int[][][]). +// Rank 1: jobjects & jstrings. +// Rank 2+: Multi-dimension arrays (e.g. int[][], int[][][]). //////////////////////////////////////////////////////////////////////////////// template struct FieldHelper< - T, kRank, true, - std::enable_if_t<(std::is_same_v || (kRank > 1))>> { - static inline jobjectArray GetValue(const jclass clazz, + T, kRank, false, + std::enable_if_t<(std::is_same_v || + std::is_same_v || (kRank > 1))>> { + static inline jobjectArray GetValue(const jobject object_ref, const jfieldID field_ref_) { - Trace(metaprogramming::LambdaToStr(STR("GetStaticObjectField, Rank 1+")), - clazz, field_ref_); + Trace(metaprogramming::LambdaToStr(STR("GetObjectField, Rank >1")), + object_ref, field_ref_); #ifdef DRY_RUN return Fake(); #else return static_cast( - jni::JniEnv::GetEnv()->GetStaticObjectField(clazz, field_ref_)); + jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_)); #endif // DRY_RUN } - static inline void SetValue(const jclass clazz, const jfieldID field_ref_, - jobjectArray&& value) { - Trace(metaprogramming::LambdaToStr(STR("SetStaticObjectField, Rank 1+")), - clazz, field_ref_, value); + static inline void SetValue(const jobject object_ref, + const jfieldID field_ref_, jobjectArray&& value) { + Trace(metaprogramming::LambdaToStr(STR("SetObjectField, Rank >1")), + object_ref, field_ref_); #ifdef DRY_RUN #else - jni::JniEnv::GetEnv()->SetStaticObjectField(clazz, field_ref_, value); + jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, value); #endif // DRY_RUN } }; @@ -6727,151 +6332,6 @@ struct FieldSelection { } // namespace jni -// IWYU pragma: private, include "third_party/jni_wrapper/jni_bind.h" - -#include -#include - -namespace jni { - -static inline jclass LoadClassFromObject(const char* name, jobject object_ref); - -// Represents a a jclass instance for a specific class. 4 flavours exist: -// 1) Default JVM, default class loader. -// 2) Non-default JVM, default class loader. -// 3) Default JVM, non-default class loader. -// 4) Non-default JVM, non default class loader (i.e. fully specified). -// -// Use |ClassRef_t| to provide |JniT| in its minimal form. -template -class ClassRef { - public: - static_assert(std::is_same_v, - "JniT must be in its minimal form for best caching."); - - template - static void PrimeJClassFromClassLoader(Lambda lambda) { - class_ref_.LoadAndMaybeInit(lambda); - } - - static jclass GetAndMaybeLoadClassRef( - jobject optional_object_to_build_loader_from) { - // For the default classloader, storage in uniquely IDed struct static. - if constexpr (JniT::GetClassLoader() == kDefaultClassLoader) { - static auto get_lambda = - [](metaprogramming::DoubleLockedValue* storage) { - if (kConfiguration.release_class_ids_on_teardown_) { - DefaultRefs().push_back(storage); - } - - // FindClass uses plain name (e.g. "kClass") for rank 0, qualified - // class names when used in arrays (e.g. "[LkClass;"). This doesn't - // come up in the API until rank 2. - if constexpr (JniT::kRank <= 1) { - return static_cast( - LifecycleHelper::Promote( - JniHelper::FindClass(JniT::kName.data()))); - } else { - // Primitive types drop their rank by 1 because of how their - // signatures get derived in array_ref.h. - using JniTForLifecycle = std::conditional_t< - std::is_same_v, JniT, - typename JniT::RankLess1>; - - return static_cast( - LifecycleHelper::Promote( - JniHelper::FindClass( - SelectorStaticInfo< - JniTSelector>::TypeName() - .data()))); - } - }; - - return RefStorage< - decltype(get_lambda), - SelectorStaticInfo>>::Get(get_lambda); - } else { - // For non default classloader, storage in class member. - return class_ref_.LoadAndMaybeInit([=]() { - return LoadClassFromObject(JniT::kNameWithDots.data(), - optional_object_to_build_loader_from); - }); - } - } - - static jclass GetAlreadyLoadedClassRef() { - return class_ref_.LoadAndMaybeInit([]() { return jclass{0}; }); - } - - static void MaybeReleaseClassRef() { - class_ref_.Reset([](jclass maybe_loaded_class) { - LifecycleHelper::Delete( - maybe_loaded_class); - }); - } - - private: - // A global reference to a jclass object that is returned from FindClass. - // The variable has static storage because ClassIDs are static to the lifetime - // of a JVM. See GetAndMaybeLoadClassRef and MaybeReleaseClassRef. - static inline metaprogramming::DoubleLockedValue class_ref_; -}; - -// When we get an object_ref_ as a return value from a Java method, it may be -// an instance of a subclass of ClassRefT. In this case, if we directly used -// the object_ref_'s class, then we might incorrectly get member information -// for the subclass instead of the original class. However, the original class -// should still be loadable from the subclass's class loader, so we load the -// ClassRef explicitly by class name. -static inline jclass LoadClassFromObject(const char* name, jobject object_ref) { - // We cannot refer to the wrapper MethodRefs here, so we just manually use - // the class loader through JNI. - - // Gets the ClassLoader of java/lang/class (the primordial loader). - // Note, these aren't static methods, they're member methods to be invoked - // on the object's class itself. The class may not have been loaded yet, - // and all you have is a jobject - jclass java_lang_class_jclass = - ClassRef>::GetAndMaybeLoadClassRef(nullptr); - - jclass java_lang_class_loader_jclass = - ClassRef>::GetAndMaybeLoadClassRef( - nullptr); - - jclass class_of_object_jclass = JniHelper::GetObjectClass(object_ref); - - jmethodID get_class_loader_jmethod = JniHelper::GetMethodID( - java_lang_class_jclass, "getClassLoader", "()Ljava/lang/ClassLoader;"); - - jobject object_ref_class_loader_jobject = - InvokeHelper::Invoke(class_of_object_jclass, nullptr, - get_class_loader_jmethod); - - jmethodID load_class_jmethod = - JniHelper::GetMethodID(java_lang_class_loader_jclass, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - - jstring name_string = - LifecycleHelper::Construct(name); - jobject local_jclass_of_correct_loader = - InvokeHelper::Invoke(object_ref_class_loader_jobject, - nullptr, load_class_jmethod, - name_string); - jobject promote_jclass_of_correct_loader = - LifecycleHelper::Promote( - local_jclass_of_correct_loader); - - LifecycleHelper::Delete( - object_ref_class_loader_jobject); - - return static_cast(promote_jclass_of_correct_loader); -} - -template -using ClassRef_t = ClassRef; - -} // namespace jni - #include #include #include @@ -6899,13 +6359,123 @@ static constexpr bool StringContains_v = } // namespace jni::metaprogramming +#include +#include +#include +#include +#include + namespace jni::metaprogramming { -enum class PackType { - NOT_CONTAINER, - TYPES, - AUTO, - AUTO_REF, +template +class QueryableMapBase {}; + +// This is an interface that can be inherited from to expose an +// operator["name"]. It provides compile time string index lookup with no macros +// although it is dependent on a clang extension. +// +// To use this API, inherit from this class using template types as follows: +// +// |CrtpBase|: The name of the class inheriting from the map. This class +// will inherit an operator[]. It must implement this exact signature: +// +// template +// auto QueryableMapCall(const char* key); +// +// |tup_container_v| is a static instance of an object whose |nameable_member| +// contains a public field called name_. It might seem strange not to +// directly pass a const auto&, however, this prevents accessing subobjects. +// +// The motivation for using inheritance as opposed to a simple member is that +// the the const char cannot be propagated without losing its constexpr-ness, +// and so the clang extension can no longer restrict function candidates. +template +class QueryableMap + : public QueryableMapBase> {}; + +template ::* nameable_member> +using QueryableMap_t = + QueryableMap>, + std::decay_t, nameable_member>; + +template +class QueryableMapEntry; + +template +class QueryableMapBase> + : public QueryableMapEntry... { + public: + using QueryableMapEntry::operator[]...; + + using QueryableMapEntry::Contains...; + + // Will select subclass specialisations if present. + constexpr bool Contains(const char* key) { return false; } +}; + +template +class QueryableMapEntry { + public: +#if __clang__ + // This function blurs the distinction between type and value space. The + // clang extension allows the key to be wrapped in a constexpr way. This + // allows for string to string comparison based on the static value the class + // is templated by. + // + // The reason the TypeMap interface requires inheritance as opposed to simply + // holding an instance of this map (like you would with a regular hash map) is + // the constexpr-ness of the string can't be propagated. This essentially + // means you get one shot at defining the function. + constexpr auto operator[](const char* key) __attribute__(( + enable_if(std::string_view(key) == + std::get(tup_container_v.*nameable_member).name_, + ""))) { + static_assert(std::is_base_of_v, + "You must derive from the invocable map."); + + return (*static_cast(this)).template QueryableMapCall(key); + } + + constexpr bool Contains(const char* key) __attribute__(( + enable_if(std::string_view(key) == + std::get(tup_container_v.*nameable_member).name_, + ""))) { + return true; + } +#else + static_assert(false, + "This container requires clang for compile time strings."); +#endif +}; + +} // namespace jni::metaprogramming + +namespace jni::metaprogramming { + +enum class PackType { + NOT_CONTAINER, + TYPES, + AUTO, + AUTO_REF, CONST_AUTO_REF, }; @@ -6947,10 +6517,10 @@ static constexpr PackType PackDiscriminator_e = PackDiscrimator::template val; // Metafunction to forward a containerized pack to a compatible container. -template