From 1cf0e8d0a925e36823775122bdb88ed6ff324879 Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Fri, 23 Jun 2023 13:12:18 -0700 Subject: [PATCH] Rev JNI Bind release header to v 0.9.6. PiperOrigin-RevId: 542939822 --- README.md | 6 +- jni_bind_release.h | 8922 ++++++++++++++++++----------------- jni_bind_release_leader.inc | 2 +- 3 files changed, 4549 insertions(+), 4381 deletions(-) diff --git a/README.md b/README.md index d9d917de..4a280e59 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ It requires clang enabled at C++17 or later, is compatible with Android, and is unit / E2E tested on `x86`/`ARM` toolchains. **Many** features and optimisations are included: + - **Object Management** - **Compile time method name and argument checking** - **Static caching of IDs** *multi-threading support and compile time signature generation.* @@ -82,8 +83,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-0.9.5-alpha.zip"], - strip_prefix = "jni-bind-Release-0.9.5-alpha", + urls = ["https://github.com/google/jni-bind/archive/refs/tags/Release-0.9.6-alpha.zip"], + strip_prefix = "jni-bind-Release-0.9.6-alpha", ) ``` @@ -474,6 +475,7 @@ Sample [local_array.h](implementation/local_array_test.cc), [array_test_jni.cc]( ## Upcoming Features Feature requests are welcome! Some upcoming features are: + - Statics - Tighter array type validation (arrays are overly permissive for arguments) - Better error messages diff --git a/jni_bind_release.h b/jni_bind_release.h index b895e4af..68bd68ae 100644 --- a/jni_bind_release.h +++ b/jni_bind_release.h @@ -1400,6 +1400,40 @@ class ConstructorRef : public ConstructorBase { } // namespace jni +#include +#include + +namespace jni::metaprogramming { + +struct StringConcatenate { + template + struct Helper { + static constexpr auto BuildConcatenation() noexcept { + constexpr std::size_t len = (Vs.size() + ... + 0); + std::array arr{}; + auto append_single_string = + [i = 0, &arr](auto const& string_to_concatenate) mutable { + for (auto c : string_to_concatenate) arr[i++] = c; + }; + (append_single_string(Vs), ...); + arr[len] = 0; + + return arr; + } + + static constexpr auto arr = BuildConcatenation(); + static constexpr std::string_view value{arr.data(), arr.size() - 1}; + }; + + template + static constexpr std::string_view value = Helper::value; +}; + +template +static constexpr auto StringConcatenate_v = StringConcatenate::value; + +} // namespace jni::metaprogramming + #include #include #include @@ -1590,6 +1624,101 @@ using StorageHelper_t = typename StorageHelper::type; } // namespace jni +namespace jni::metaprogramming { + +template +struct RepeatString { + template + static constexpr std::string_view val = metaprogramming::StringConcatenate_v< + val_to_repeat, RepeatString::template val>; +}; + +template <> +struct RepeatString<0> { + template + static constexpr std::string_view val{""}; +}; + +template +static constexpr auto RepeatString_v{ + RepeatString::template val}; + +} // namespace jni::metaprogramming + +#include + +namespace jni::metaprogramming { + +struct Constants { + static constexpr std::string_view new_line = "\n"; + + static constexpr std::string_view left_bracket = "["; + static constexpr std::string_view right_bracket = "]"; + + static constexpr std::string_view left_parenthesis = "("; + static constexpr std::string_view right_parenthesis = ")"; + + static constexpr std::string_view hash = "#"; + static constexpr std::string_view comma = ","; + static constexpr std::string_view semi_colon = ";"; + + static constexpr std::string_view a = "a"; + static constexpr std::string_view b = "b"; + static constexpr std::string_view c = "c"; + static constexpr std::string_view d = "d"; + static constexpr std::string_view e = "e"; + static constexpr std::string_view f = "f"; + static constexpr std::string_view g = "g"; + static constexpr std::string_view h = "h"; + static constexpr std::string_view i = "i"; + static constexpr std::string_view j = "j"; + static constexpr std::string_view k = "k"; + static constexpr std::string_view l = "l"; + static constexpr std::string_view m = "m"; + static constexpr std::string_view n = "n"; + static constexpr std::string_view o = "o"; + static constexpr std::string_view p = "p"; + static constexpr std::string_view q = "q"; + static constexpr std::string_view r = "r"; + static constexpr std::string_view s = "s"; + static constexpr std::string_view t = "t"; + static constexpr std::string_view u = "u"; + static constexpr std::string_view v = "v"; + static constexpr std::string_view w = "w"; + static constexpr std::string_view x = "x"; + static constexpr std::string_view y = "y"; + static constexpr std::string_view z = "z"; + + static constexpr std::string_view A = "A"; + static constexpr std::string_view B = "B"; + static constexpr std::string_view C = "C"; + static constexpr std::string_view D = "D"; + static constexpr std::string_view E = "E"; + static constexpr std::string_view F = "F"; + static constexpr std::string_view G = "G"; + static constexpr std::string_view H = "H"; + static constexpr std::string_view I = "I"; + static constexpr std::string_view J = "J"; + static constexpr std::string_view K = "K"; + static constexpr std::string_view L = "L"; + static constexpr std::string_view M = "M"; + static constexpr std::string_view N = "N"; + static constexpr std::string_view O = "O"; + static constexpr std::string_view P = "P"; + static constexpr std::string_view Q = "Q"; + static constexpr std::string_view R = "R"; + static constexpr std::string_view S = "S"; + static constexpr std::string_view T = "T"; + static constexpr std::string_view U = "U"; + static constexpr std::string_view V = "V"; + static constexpr std::string_view W = "W"; + static constexpr std::string_view X = "X"; + static constexpr std::string_view Y = "Y"; + static constexpr std::string_view Z = "Z"; +}; + +} // namespace jni::metaprogramming + #include #include #include @@ -1631,73 +1760,202 @@ constexpr std::size_t ModifiedMax( } // namespace jni::metaprogramming - -#include +#include namespace jni { -template -struct Array; +// Translates a single JNI term (e.g. jint -> "I") as if it were being used as a +// parameter to a method. +// +// Note, the context a parameter is used on occasion will alter a signature, +// e.g. void in a return is explicit, whereas when used as a parameter, it is +// represented as the omission of any value. +// +// Additionally, Android will obnoxiously fail to compile the standard looking: +// static constexpr char kStr[] = "SomeString"; +// +// Because it is against style to import a using declaration header wide, +// but these are also template definitions, they must remain in this header, and +// so there are goofy looking "using literal" declarations throughout. +// +// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html +// +// TODO: Rename to JavaPrimitiveTypeToString +template +constexpr std::string_view JavaTypeToString(); -template -struct Rank {}; +template <> +constexpr std::string_view JavaTypeToString() { + // Note: This only applies when used as a return, not as a parameter. This + // could be enforced through type system, but maybe feels excessive to do so. + // For now, enforcing this is unnecesssary, as this function is only called + // for each Param, which, in the case of no params, is 0 times. + using namespace std::literals; + return "V"sv; +} -//////////////////////////////////////////////////////////////////////////////// -// Array Non-Object Implementation. -//////////////////////////////////////////////////////////////////////////////// -template -struct ArrayNonObjectTypeImpl { - RawType raw_; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "Z"sv; +} - constexpr ArrayNonObjectTypeImpl(RawType raw) : raw_(raw) {} +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "B"sv; +} - constexpr ArrayNonObjectTypeImpl(RawType raw, Rank) : raw_(raw) {} +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "C"sv; +} - template - constexpr ArrayNonObjectTypeImpl(Array&& invalid_arg) - : raw_(nullptr) { - static_assert(std::is_same_v, - "JNI Error: Invalid array declaration, use Array { type{}, " - "Rank{} }."); - } +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "S"sv; +} - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { - return (raw_ == rhs.raw_); - } - return false; - } +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "I"sv; +} - template - constexpr bool operator!=(const Array& rhs) const { - return !(*this == rhs); - } -}; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "J"sv; +} -// Primitive array implementaiton. -template -struct ArrayImpl : public ArrayNonObjectTypeImpl, - ArrayTag> { - public: - using ArrayNonObjectTypeImpl::ArrayNonObjectTypeImpl; -}; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "F"sv; +} -//////////////////////////////////////////////////////////////////////////////// -// Array Object Implementation. -//////////////////////////////////////////////////////////////////////////////// -template -struct ArrayImpl : public ArrayTag { - RawType raw_; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "D"sv; +} - constexpr ArrayImpl(RawType raw) : raw_(raw) {} +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "Ljava/lang/String;"sv; +} - template - constexpr ArrayImpl(RawType raw, Rank) : raw_(raw) {} +} // namespace jni - template - constexpr bool operator==(const Array& rhs) const { - if constexpr (std::is_same_v) { +#include +#include + +namespace jni { + +// Metafunction that returns either "" if a member called |name_| isn't +// present, or a constexpr std::string_view of the name if it is. +template +struct NameOrNothing { + static constexpr std::string_view val{""}; +}; + +template +struct NameOrNothing> { + static constexpr std::string_view val{val_.name_}; +}; + +template +static constexpr auto NameOrNothing_v = NameOrNothing::val; + +static constexpr std::string_view kInit{""}; + +} // namespace jni + +namespace jni { + +enum class IdType { + CLASS, + STATIC_OVERLOAD_SET, + STATIC_OVERLOAD, + STATIC_OVERLOAD_PARAM, + OVERLOAD_SET, + OVERLOAD, + OVERLOAD_PARAM, + STATIC_FIELD, + FIELD, +}; + +} // namespace jni + +#include + +namespace jni { + +template +struct Array; + +template +struct Rank {}; + +//////////////////////////////////////////////////////////////////////////////// +// Array Non-Object Implementation. +//////////////////////////////////////////////////////////////////////////////// +template +struct ArrayNonObjectTypeImpl { + RawType raw_; + + constexpr ArrayNonObjectTypeImpl(RawType raw) : raw_(raw) {} + + constexpr ArrayNonObjectTypeImpl(RawType raw, Rank) : raw_(raw) {} + + template + constexpr ArrayNonObjectTypeImpl(Array&& invalid_arg) + : raw_(nullptr) { + static_assert(std::is_same_v, + "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 + constexpr bool operator!=(const Array& rhs) const { + return !(*this == rhs); + } +}; + +// Primitive array implementaiton. +template +struct ArrayImpl : public ArrayNonObjectTypeImpl, + ArrayTag> { + public: + using ArrayNonObjectTypeImpl::ArrayNonObjectTypeImpl; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Array Object Implementation. +//////////////////////////////////////////////////////////////////////////////// +template +struct ArrayImpl : public ArrayTag { + 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; @@ -1784,549 +2042,519 @@ constexpr auto FullArrayStripV(const T& val) { } // namespace jni #include +#include -namespace jni { - -// The set of classes that a ClassLoader can load. -template -class SupportedClassSet { - public: - const std::tuple supported_classes_; +namespace jni::metaprogramming { - constexpr SupportedClassSet(Classes_... supported_classes) - : supported_classes_(supported_classes...) { - // TODO(b/143908983): Classloaders should enforce unique classes. - // static_assert(metaprogramming::AllUniqueValues(supported_classes...), - //"All classes supported by the class loader must be unique."); - } -}; +// Metafunction to take a sequence of indices, and produce the corresponding +// elements in sequences of tuples. +template +struct TypeIndexMask { + template + struct GetHelper {}; -template -SupportedClassSet(Classes...) -> SupportedClassSet; + template + struct GetHelper, Ts...> { + using type = std::tuple...>; + }; -} // namespace jni + template + using Get = typename GetHelper, Ts...>::type; + template + struct SequenceGenerator {}; -#include + template + struct SequenceGenerator> { + using type = Get; + }; -namespace jni { + template + using type = typename SequenceGenerator::type; +}; -static constexpr std::size_t kDefaultClassLoaderIdx = 0; -static constexpr std::size_t kClassNotInLoaderSetIdx = - metaprogramming::kNegativeOne; +template +using TypeTupFromIndexMask_t = + typename TypeIndexMask::template type; -// Class loader that can supply any class (to be used when none is specified). -// Setting this as the root loader for user defined classes will disable checks -// that classes are explicitly listed under a loader's class list. -class DefaultClassLoader { - public: - const char* name_ = "__JNI_BIND_DEFAULT_CLASS_LOADER__"; - std::tuple<> supported_classes_{}; +} // namespace jni::metaprogramming - // Note, this will return true iff ignore_default_loader is true, but the - // corresponding IdxOfAncestor will be kClassNotInLoaderSetIdx. - template - constexpr bool SupportedDirectlyOrIndirectly() const { - if constexpr (ignore_default_loader) { - // Note, it's an implementation detail that kDefaultClassLoader returns - // false for |SupportedByThisSet|. Otherwise, all classes will always - // defer to the default loader. See |ParentLoaderForClass|. - return false; - } else { - return true; - } - } +#include +#include - template - constexpr std::size_t IdxOfClass() const { - return kClassNotInLoaderSetIdx; - } +namespace jni::metaprogramming { - template - constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { - return kClassNotInLoaderSetIdx; - } +// Represents a value that can be [0, max], and an overflow bit. +// When incremented, the type will rollover, and set its overflow bit. +template +struct NBit { + static constexpr size_t value_ = cur_value; + static constexpr size_t overflow_bit_ = overflow_bit; - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const DefaultClassLoader&) const { return true; } + // Only unary values will ever need the clamp. + using Increment = + NBit<(value_ == max ? 0 : std::clamp(value_ + 1, size_t{0}, max)), max, + value_ == max>; - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } + using ResetOverflow = NBit; }; -// Class loader that cannot supply any classes. This should be the root loader -// for most user defined classes. -class NullClassLoader { - public: - const char* name_ = "__JNI_BIND_NULL_CLASS_LOADER__"; +} // namespace jni::metaprogramming - template - constexpr bool SupportedDirectlyOrIndirectly() const { - return false; - } +#include +#include +#include - template - constexpr std::size_t IdxOfClass() const { - return kNoIdx; - } +#define STR(x) []() { return x; } - template - constexpr std::size_t IdxOfAncestor(std::size_t cur_idx = 0) const { - return kClassNotInLoaderSetIdx; - } +namespace jni::metaprogramming { - template - bool constexpr operator==(const T& rhs) const { - return false; - } - bool constexpr operator==(const NullClassLoader&) const { return true; } +template +using identifier_type = decltype(std::declval()()); - template - bool constexpr operator!=(const T& rhs) const { - return !(*this == rhs); - } -}; +constexpr std::size_t ConstexprStrlen(const char* str) { + return str[0] == 0 ? 0 : ConstexprStrlen(str + 1) + 1; +} -static constexpr NullClassLoader kNullClassLoader; -static constexpr DefaultClassLoader kDefaultClassLoader; +struct StringAsTypeBase {}; -// DO NOT USE: This obviates a compiler bug for value based enablement on ctor. -static constexpr auto kShadowNullClassLoader = kNullClassLoader; +// Represents a string by embedding a sequence of characters in a type. +template +struct StringAsType : StringAsTypeBase { + static constexpr char static_chars[] = {chars..., 0}; + static constexpr std::string_view chars_as_sv = {static_chars, + sizeof...(chars)}; +}; -// DO NOT USE: This obviates a compiler bug for value based enablement on ctor. -static constexpr auto kShadowDefaultClassLoader = kDefaultClassLoader; - -} // namespace jni - - -namespace jni { - -// clang-format off -inline constexpr Class kJavaLangClass{"java/lang/Class"}; - -inline constexpr Class kJavaLangObject{"java/lang/Object"}; - -inline constexpr Class kJavaLangClassLoader{ - "java/lang/ClassLoader", - Method{"loadClass", Return{kJavaLangClass}, Params{}}, - Method{"toString", Return{jstring{}}, Params<>{}}, -}; - -static constexpr Class kJavaLangString{ - "java/lang/String", +template +constexpr auto LambdaToStr(Identifier id, std::index_sequence) { + return StringAsType{}; +} - Constructor{jstring{}}, - Constructor{Array{jbyte{}}}, +template < + typename Identifier, + std::enable_if_t, const char*>, + int> = 0> +constexpr auto LambdaToStr(Identifier id) { + return LambdaToStr(id, std::make_index_sequence{}); +} - Method{"toString", Return{jstring{}}, Params<>{}}, -}; -// clang-format on +template +using LambdaStringToType = decltype(LambdaToStr(std::declval())); -} // namespace jni +} // namespace jni::metaprogramming -#include -#include +#include #include namespace jni::metaprogramming { -// Wrapper to convert a sequence of values into a type. -template -struct Val { - using type = decltype(val_); - static constexpr type val = val_; -}; - -template -using Val_t = Val; - -// Wrapper to convert a sequence of values into a type. -template -struct Vals { - static constexpr std::array val{Vs...}; -}; +// Metafunction to increment types a certain number of times. +// Types must export a type alias "Increment" which increments the type once. +template +struct Increment { + template + struct IncrementCountHelper { + using type = typename Increment::template type::Increment; + }; -// Wrapper to convert a sequence of const values into a type. -template -struct ValsConst { - static constexpr std::array val{Vs...}; -}; + template + struct IncrementCountHelper { + using type = T; + }; -// Wrapper to convert a sequence of ref values into a type. -template -struct ValsRef { - static constexpr std::array val{Vs...}; + template + using type = typename IncrementCountHelper::type; }; -// Wrapper to convert a sequence of const ref values into a type. -template -struct ValsConstRef { - static constexpr std::array val{Vs...}; -}; +template +using Increment_t = typename Increment::template type; } // namespace jni::metaprogramming -#include -#include +#include +#include namespace jni::metaprogramming { -struct StringConcatenate { - template +// Metafunction that detects a partial specialisation for a Container. +template