From fa8dacdd00ea1cadbdf60a591877e45670982a99 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 20 Feb 2022 19:33:23 +0800 Subject: [PATCH] Add array oprations to JNIHelper --- library/jni/art/runtime/art_method.hpp | 9 +- library/jni/include/utils/jni_helper.hpp | 372 +++++++++++++++++++---- library/jni/lsplant.cc | 4 +- 3 files changed, 321 insertions(+), 64 deletions(-) diff --git a/library/jni/art/runtime/art_method.hpp b/library/jni/art/runtime/art_method.hpp index 0c337a2..8cfda2c 100644 --- a/library/jni/art/runtime/art_method.hpp +++ b/library/jni/art/runtime/art_method.hpp @@ -140,15 +140,14 @@ public: static_assert(std::is_same_v); jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); - auto constructors = + const auto constructors = JNI_Cast(JNI_CallObjectMethod(env, throwable, get_declared_constructors)); - auto length = JNI_GetArrayLength(env, constructors); - if (length < 2) { + if (constructors.size() < 2) { LOGE("Throwable has less than 2 constructors"); return false; } - auto first_ctor = JNI_GetObjectArrayElement(env, constructors, 0); - auto second_ctor = JNI_GetObjectArrayElement(env, constructors, 1); + auto &first_ctor = constructors[0]; + auto &second_ctor = constructors[1]; auto *first = FromReflectedMethod(env, first_ctor.get()); auto *second = FromReflectedMethod(env, second_ctor.get()); art_method_size = reinterpret_cast(second) - reinterpret_cast(first); diff --git a/library/jni/include/utils/jni_helper.hpp b/library/jni/include/utils/jni_helper.hpp index bb1a3dc..e3d1c5c 100644 --- a/library/jni/include/utils/jni_helper.hpp +++ b/library/jni/include/utils/jni_helper.hpp @@ -1,6 +1,3 @@ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" #pragma once #include @@ -9,6 +6,11 @@ #include #include +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-partial-specialization" +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" + #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName &) = delete; \ void operator=(const TypeName &) = delete @@ -31,14 +33,14 @@ class ScopedLocalRef { public: using BaseType [[maybe_unused]] = T; - ScopedLocalRef(JNIEnv *env, T localRef) : env_(env), local_ref_(localRef) {} + ScopedLocalRef(JNIEnv *env, T local_ref) : env_(env), local_ref_(nullptr) { reset(local_ref); } - ScopedLocalRef(ScopedLocalRef &&s) noexcept : env_(s.env_), local_ref_(s.release()) {} + ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, s.release()) {} template - ScopedLocalRef(ScopedLocalRef &&s) noexcept : env_(s.env_), local_ref_((T)s.release()) {} + ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} - explicit ScopedLocalRef(JNIEnv *env) noexcept : env_(env), local_ref_(nullptr) {} + explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} ~ScopedLocalRef() { reset(); } @@ -85,6 +87,12 @@ private: DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); }; +template +concept JArray = std::is_base_of_v, std::remove_pointer_t>; + +template +class ScopedLocalRef; + class JNIScopeFrame { JNIEnv *env_; @@ -727,6 +735,307 @@ template std::forward(clazz), method, std::forward(args)...); } +template +[[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, Class &&clazz, jmethodID method, + Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::NewObject, std::forward(clazz), method, + std::forward(args)...); +} + +template +[[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, std::forward(args)...); +} + +template +[[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, Class &&clazz, + const JNINativeMethod *methods, jint size) { + return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, std::forward(clazz), methods, size); +} + +template +[[maybe_unused]] inline auto JNI_IsInstanceOf(JNIEnv *env, Object &&obj, Class &&clazz) { + return JNI_SafeInvoke(env, &JNIEnv::IsInstanceOf, std::forward(obj), + std::forward(clazz)); +} + +template +[[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { + return (decltype(UnwrapScope(std::forward(x))))env->NewGlobalRef( + UnwrapScope(std::forward(x))); +} + +template +[[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef &&x) requires( + std::is_convertible_v) { + return ScopedLocalRef(std::move(x)); +} + +[[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, void *address, jlong capacity) { + return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, address, capacity); +} + +template +struct JArrayUnderlyingTypeHelper; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = ScopedLocalRef; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jboolean; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jbyte; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jchar; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jshort; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jint; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jlong; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jfloat; +}; + +template <> +struct JArrayUnderlyingTypeHelper { + using Type = jdouble; +}; + +template +using JArrayUnderlyingType = typename JArrayUnderlyingTypeHelper::Type; + +template +class ScopedLocalRef { + ScopedLocalRef(JNIEnv *env, T local_ref, size_t size, JArrayUnderlyingType *elements, + bool modified) noexcept + : env_(env), local_ref_(local_ref), size_(size), elements_(elements), modified_(modified) {} + +public: + class Iterator { + friend class ScopedLocalRef; + Iterator(JArrayUnderlyingType *e) : e_(e) {} + JArrayUnderlyingType *e_; + + public: + auto &operator*() { return *e_; } + auto *operator->() { return e_; } + Iterator &operator++() { return ++e_, *this; } + Iterator &operator--() { return --e_, *this; } + Iterator operator++(int) { return Iterator(e_++); } + Iterator operator--(int) { return Iterator(e_--); } + bool operator==(const Iterator &other) const { return other.e_ == e_; } + bool operator!=(const Iterator &other) const { return other.e_ == e_; } + }; + + class ConstIterator { + friend class ScopedLocalRef; + ConstIterator(const JArrayUnderlyingType *e) : e_(e) {} + const JArrayUnderlyingType *e_; + + public: + const auto &operator*() { return *e_; } + const auto *operator->() { return e_; } + ConstIterator &operator++() { return ++e_, *this; } + ConstIterator &operator--() { return --e_, *this; } + ConstIterator operator++(int) { return ConstIterator(e_++); } + ConstIterator operator--(int) { return ConstIterator(e_--); } + bool operator==(const ConstIterator &other) const { return other.e_ == e_; } + bool operator!=(const ConstIterator &other) const { return other.e_ == e_; } + }; + + auto begin() { + modified_ = true; + return Iterator(elements_); + } + + auto end() { + modified_ = true; + return Iterator(elements_ + size_); + } + + const auto begin() const { return ConstIterator(elements_); } + + auto end() const { return ConstIterator(elements_ + size_); } + + const auto cbegin() const { return ConstIterator(elements_); } + + auto cend() const { return ConstIterator(elements_ + size_); } + + using BaseType [[maybe_unused]] = T; + + ScopedLocalRef(JNIEnv *env, T local_ref) noexcept : env_(env), local_ref_(nullptr) { + reset(local_ref); + } + + ScopedLocalRef(ScopedLocalRef &&s) noexcept + : ScopedLocalRef(s.env_, s.local_ref_, s.size_, s.elements_, s.modified_) { + s.local_ref_ = nullptr; + s.size_ = 0; + s.elements_ = nullptr; + s.modified_ = false; + } + + template + ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} + + explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} + + ~ScopedLocalRef() { release(); } + + void reset(T ptr = nullptr) { + if (ptr != local_ref_) { + if (local_ref_ != nullptr) { + ReleaseElements(modified_ ? 0 : JNI_ABORT); + env_->DeleteLocalRef(local_ref_); + if constexpr (std::is_same_v) { + for (size_t i = 0; i < size_; ++i) { + elements_[i].~ScopedLocalRef(); + } + operator delete[](elements_); + } + elements_ = nullptr; + } + local_ref_ = ptr; + size_ = local_ref_ ? env_->GetArrayLength(local_ref_) : 0; + if (!local_ref_) return; + if constexpr (std::is_same_v) { + elements_ = static_cast *>(operator new[]( + sizeof(ScopedLocalRef) * size_)); + for (size_t i = 0; i < size_; ++i) { + new (&elements_[i]) ScopedLocalRef( + JNI_SafeInvoke(env_, &JNIEnv::GetObjectArrayElement, local_ref_, i)); + } + } else if constexpr (std::is_same_v) { + elements_ = env_->GetBooleanArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetByteArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetCharArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetShortArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetIntArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetLongArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetFloatArrayElements(local_ref_, nullptr); + } else if constexpr (std::is_same_v) { + elements_ = env_->GetDoubleArrayElements(local_ref_, nullptr); + } + } + } + + [[nodiscard]] T release() { + T localRef = local_ref_; + size_ = 0; + local_ref_ = nullptr; + ReleaseElements(modified_ ? 0 : JNI_ABORT); + if constexpr (std::is_same_v) { + for (size_t i = 0; i < size_; ++i) { + elements_[i].~ScopedLocalRef(); + } + operator delete[](elements_); + } + elements_ = nullptr; + return localRef; + } + + T get() const { return local_ref_; } + + explicit operator T() const { return local_ref_; } + + JArrayUnderlyingType &operator[](size_t index) { + modified_ = true; + return elements_[index]; + } + + const JArrayUnderlyingType &operator[](size_t index) const { return elements_[index]; } + + void commit() { ReleaseElements(JNI_COMMIT); } + + // We do not expose an empty constructor as it can easily lead to errors + // using common idioms, e.g.: + // ScopedLocalRef<...> ref; + // ref.reset(...); + // Move assignment operator. + ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { + env_ = s.env_; + local_ref_ = s.local_ref_; + size_ = s.size_; + elements_ = s.elements_; + modified_ = s.modified_; + s.elements_ = nullptr; + s.size_ = 0; + s.modified_ = false; + s.local_ref_ = nullptr; + return *this; + } + + size_t size() const { return size_; } + + operator bool() const { return local_ref_; } + + template + friend class ScopedLocalRef; + + friend class JUTFString; + +private: + void ReleaseElements(jint mode) { + if (!local_ref_ || !elements_) return; + if constexpr (std::is_same_v) { + for (size_t i = 0; i < size_; ++i) { + JNI_SafeInvoke(env_, &JNIEnv::SetObjectArrayElement, local_ref_, i, elements_[i]); + } + } else if constexpr (std::is_same_v) { + env_->ReleaseBooleanArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseByteArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseCharArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseShortArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseIntArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseLongArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseFloatArrayElements(local_ref_, elements_, mode); + } else if constexpr (std::is_same_v) { + env_->ReleaseDoubleArrayElements(local_ref_, elements_, mode); + } + } + + JNIEnv *env_; + T local_ref_; + size_t size_; + JArrayUnderlyingType *elements_{nullptr}; + bool modified_ = false; + DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); +}; + // functions to array template Array> @@ -773,55 +1082,6 @@ template [[maybe_unused]] inline auto JNI_NewDoubleArray(JNIEnv *env, jsize len) { return JNI_SafeInvoke(env, &JNIEnv::NewDoubleArray, len); } - -template Array> -[[maybe_unused]] inline auto JNI_GetObjectArrayElement(JNIEnv *env, const Array &array, - jsize index) { - return JNI_SafeInvoke(env, &JNIEnv::GetObjectArrayElement, array, index); -} - -template Array, ScopeOrObject Object> -[[maybe_unused]] inline auto JNI_SetObjectArrayElement(JNIEnv *env, const Array &array, jsize index, - Object &&obj) { - return JNI_SafeInvoke(env, &JNIEnv::SetObjectArrayElement, array, index, obj); -} - -template -[[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::NewObject, std::forward(clazz), method, - std::forward(args)...); -} - -template -[[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, void *address, jlong capacity) { - return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, address, capacity); -} - -template -[[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, Class &&clazz, - const JNINativeMethod *methods, jint size) { - return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, std::forward(clazz), methods, size); -} - -template -[[maybe_unused]] inline auto JNI_IsInstanceOf(JNIEnv *env, Object &&obj, Class &&clazz) { - return JNI_SafeInvoke(env, &JNIEnv::IsInstanceOf, std::forward(obj), - std::forward(clazz)); -} - -template -[[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { - return (decltype(UnwrapScope(std::forward(x))))env->NewGlobalRef( - UnwrapScope(std::forward(x))); -} - -template -[[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef &&x) requires( - std::is_convertible_v) { - return ScopedLocalRef(std::move(x)); -} - } // namespace lsplant #undef DISALLOW_COPY_AND_ASSIGN diff --git a/library/jni/lsplant.cc b/library/jni/lsplant.cc index edbb28b..716bec3 100644 --- a/library/jni/lsplant.cc +++ b/library/jni/lsplant.cc @@ -643,9 +643,7 @@ using ::lsplant::IsHooked; uint8_t access_flags = JNI_GetIntField(env, target, class_access_flags); constexpr static uint32_t kAccFinal = 0x0010; JNI_SetIntField(env, target, class_access_flags, static_cast(access_flags & ~kAccFinal)); - auto len = constructors ? JNI_GetArrayLength(env, constructors) : 0; - for (auto i = 0; i < len; ++i) { - auto constructor = JNI_GetObjectArrayElement(env, constructors, i); + for (auto &constructor : constructors) { auto *method = ArtMethod::FromReflectedMethod(env, constructor.get()); if (method && (!method->IsPublic() || !method->IsProtected())) method->SetProtected(); if (method && method->IsFinal()) method->SetNonFinal();