From 80d4d55e250af1d643805bde3821987112cf09c1 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 14 Sep 2023 21:15:20 +0200 Subject: JNI: add QJniArray class for easier working with arrays Implements an iterator API and other standard member access functions for sequential containers so that we can use ranged-for over an object that is a jarray. Provides read-only access to individual elements (which is mostly relevant for arrays of objects), or the entire data() as a contiguous memory block (which is useful for arrays of primitive types). QJniObject call functions can return QJniArray when the return type is either explicitly QJniArray or T[], or their Qt equivalent (e.g. a jbyteArray can be taken or returned as a QByteArray). If the return type is a jarray type, then a QJniObject is returned as before. Arrays can be created from a Qt container through a constructor or the generic fromData named constructor in the QJniArrayBase class, which implements the generic logic. Not documented as public API yet. Added a compile-time test to verify that types are mapped correctly. The function test coverage is added to the QJniObject auto-test, as that already provides the Java test class with functions taking and returning arrays of different types. Change-Id: I0750fc4f4cce7314df3b10e122eafbcfd68297b6 Reviewed-by: Assam Boudjelthia --- src/corelib/CMakeLists.txt | 1 + src/corelib/kernel/qjniarray.h | 356 +++++++++++++++++++++ src/corelib/kernel/qjniobject.h | 77 +++-- tests/auto/corelib/kernel/CMakeLists.txt | 1 + tests/auto/corelib/kernel/qjniarray/CMakeLists.txt | 13 + .../corelib/kernel/qjniarray/tst_qjniarray.cpp | 75 +++++ .../qt/android/testdata/QtJniObjectTestClass.java | 99 ++++++ .../corelib/kernel/qjniobject/tst_qjniobject.cpp | 165 ++++++++++ .../corelib/kernel/qjnitypes/tst_qjnitypes.cpp | 8 +- 9 files changed, 770 insertions(+), 25 deletions(-) create mode 100644 src/corelib/kernel/qjniarray.h create mode 100644 tests/auto/corelib/kernel/qjniarray/CMakeLists.txt create mode 100644 tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index ca75bdf984..756d12f0bd 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -1000,6 +1000,7 @@ qt_internal_extend_target(Core CONDITION ANDROID SOURCES io/qstandardpaths_android.cpp io/qstorageinfo_linux.cpp io/qstorageinfo_linux_p.h + kernel/qjniarray.h kernel/qjnitypes.h kernel/qjnitypes_impl.h kernel/qjnienvironment.cpp kernel/qjnienvironment.h kernel/qjniobject.cpp kernel/qjniobject.h diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h new file mode 100644 index 0000000000..ba79e27b8e --- /dev/null +++ b/src/corelib/kernel/qjniarray.h @@ -0,0 +1,356 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QJNIARRAY_H +#define QJNIARRAY_H + +#include + +#if defined(Q_QDOC) || defined(Q_OS_ANDROID) +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +template class QJniArray; +template +struct QJniArrayIterator +{ + QJniArrayIterator(const QJniArrayIterator &other) = default; + QJniArrayIterator(QJniArrayIterator &&other) noexcept = default; + QJniArrayIterator &operator=(const QJniArrayIterator &other) = default; + QJniArrayIterator &operator=(QJniArrayIterator &&other) noexcept = default; + + friend bool operator==(const QJniArrayIterator &lhs, const QJniArrayIterator &rhs) noexcept + { + return lhs.m_array == rhs.m_array && lhs.m_index == rhs.m_index; + } + friend bool operator!=(const QJniArrayIterator &lhs, const QJniArrayIterator &rhs) noexcept + { + return !(lhs == rhs); + } + const T operator*() const + { + return m_array->at(m_index); + } + friend QJniArrayIterator &operator++(QJniArrayIterator &that) noexcept + { + ++that.m_index; + return that; + } + + void swap(QJniArrayIterator &other) noexcept + { + std::swap(m_index, other.m_index); + std::swap(m_array, other.m_array); + } + +private: + friend class QJniArray; + + qsizetype m_index = 0; + const QJniArray *m_array = nullptr; + + QJniArrayIterator() = delete; + QJniArrayIterator(qsizetype index, const QJniArray *array) + : m_index(index), m_array(array) + {} +}; + +class QJniArrayBase : public QJniObject +{ + // for SFINAE'ing out the fromContainer named constructor + template struct CanConvertHelper : std::false_type {}; + template + struct CanConvertHelper().data()), + decltype(std::declval().size()) + > + > : std::true_type {}; + +public: + QJniArrayBase(jarray array) + : QJniObject(static_cast(array)) + { + static_assert(sizeof(QJniArrayBase) == sizeof(QJniObject), + "QJniArrayBase must have the same size as QJniObject!"); + } + QJniArrayBase(const QJniObject &object) + : QJniObject(object) + {} + QJniArrayBase(QJniObject &&object) noexcept + : QJniObject(std::move(object)) + {} + + qsizetype size() const + { + if (jarray array = object()) + return QJniEnvironment()->GetArrayLength(array); + return 0; + } + + template + static constexpr bool CanConvert = CanConvertHelper::value; + template , bool> = true + > + static auto fromContainer(Container &&container) + { + using ElementType = typename Container::value_type; + if constexpr (std::disjunction_v, + std::is_same>) { + return makeObjectArray(std::forward(container)); + } else if constexpr (std::is_same_v) { + return makeArray(std::forward(container), &JNIEnv::NewFloatArray, + &JNIEnv::SetFloatArrayRegion); + } else if constexpr (std::is_same_v) { + return makeArray(std::forward(container), &JNIEnv::NewDoubleArray, + &JNIEnv::SetDoubleArrayRegion); + } else if constexpr (std::disjunction_v, + std::is_same>) { + return makeArray(std::forward(container), &JNIEnv::NewBooleanArray, + &JNIEnv::SetBooleanArrayRegion); + } else if constexpr (std::disjunction_v, + std::is_same>) { + return makeArray(std::forward(container), &JNIEnv::NewByteArray, + &JNIEnv::SetByteArrayRegion); + } else if constexpr (std::disjunction_v, + std::is_same>) { + return makeArray(std::forward(container), &JNIEnv::NewCharArray, + &JNIEnv::SetCharArrayRegion); + } else if constexpr (std::is_same_v + || sizeof(ElementType) == sizeof(jshort)) { + return makeArray(std::forward(container), &JNIEnv::NewShortArray, + &JNIEnv::SetShortArrayRegion); + } else if constexpr (std::is_same_v + || sizeof(ElementType) == sizeof(jint)) { + return makeArray(std::forward(container), &JNIEnv::NewIntArray, + &JNIEnv::SetIntArrayRegion); + } else if constexpr (std::is_same_v + || sizeof(ElementType) == sizeof(jlong)) { + return makeArray(std::forward(container), &JNIEnv::NewLongArray, + &JNIEnv::SetLongArrayRegion); + } + } + +protected: + QJniArrayBase() = default; + + template + static auto makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion); + template + static auto makeObjectArray(List &&list); +}; + +template +class QJniArray : public QJniArrayBase +{ + friend struct QJniArrayIterator; +public: + using Type = T; + using const_iterator = const QJniArrayIterator; + + using QJniArrayBase::QJniArrayBase; + + template , bool> = true + > + explicit QJniArray(Container &&container); + ~QJniArray() = default; + + auto arrayObject() const + { + if constexpr (std::is_convertible_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else if constexpr (std::is_same_v) + return object(); + else + return object(); + } + + const_iterator begin() const { return {0, this}; } + const_iterator constBegin() const { return begin(); } + const_iterator cbegin() const { return begin(); } + const_iterator end() const { return {size(), this}; } + const_iterator constEnd() const { return {end()}; } + const_iterator cend() const { return {end()}; } + + const T operator[](qsizetype i) const { return at(i); } // const return value to disallow assignment + T at(qsizetype i) const + { + QJniEnvironment env; + if constexpr (std::is_convertible_v) { + return T{env->GetObjectArrayElement(object(), i)}; + } else { + T res = {}; + if constexpr (std::is_same_v) + env->GetByteArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetCharArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetBooleanArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetShortArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetIntArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetLongArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetFloatArrayRegion(object(), i, 1, &res); + else if constexpr (std::is_same_v) + env->GetDoubleArrayRegion(object(), i, 1, &res); + return res; + } + } + auto asContainer() const + { + QJniEnvironment env; + if constexpr (std::is_same_v) { + QList res; + res.reserve(size()); + for (auto &&element : *this) + res.append(element); + return res; + } else if constexpr (std::is_same_v) { + const qsizetype bytecount = size(); + QByteArray res(bytecount, Qt::Initialization::Uninitialized); + env->GetByteArrayRegion(object(), + 0, bytecount, reinterpret_cast(res.data())); + return res; + } else { + QList res; + res.resize(size()); + if constexpr (std::is_same_v) { + env->GetCharArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetBooleanArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetShortArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetIntArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetLongArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetFloatArrayRegion(object(), + 0, res.size(), res.data()); + } else if constexpr (std::is_same_v) { + env->GetDoubleArrayRegion(object(), + 0, res.size(), res.data()); + } else { + res.clear(); + } + return res; + } + } +}; + +template +auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion) +{ + const int length = int(list.size()); + QJniEnvironment env; + JNIEnv *jenv = env.jniEnv(); + auto localArray = (jenv->*newArray)(length); + if (env.checkAndClearExceptions()) + return QJniArray(); + + // can't use static_cast here because we have signed/unsigned mismatches + if (length) { + (jenv->*setRegion)(localArray, 0, length, + reinterpret_cast(std::as_const(list).data())); + } + return QJniArray(localArray); +}; + +template +auto QJniArrayBase::makeObjectArray(List &&list) +{ + using ElementType = typename List::value_type; + if (list.isEmpty()) + return QJniArray(); + + QJniEnvironment env; + const int length = int(list.size()); + + // this assumes that all objects in the list have the same class + jclass elementClass = nullptr; + if constexpr (std::is_same_v) + elementClass = list.first().objectClass(); + else + elementClass = env->GetObjectClass(list.first()); + auto localArray = env->NewObjectArray(length, elementClass, nullptr); + if (env.checkAndClearExceptions()) + return QJniArray(); + for (int i = 0; i < length; ++i) { + jobject object; + if constexpr (std::is_same_v) + object = list.at(i).object(); + else + object = list.at(i); + env->SetObjectArrayElement(localArray, i, object); + } + return QJniArray(localArray); +} + + +template +template , bool> +> +QJniArray::QJniArray(Container &&container) + : QJniArrayBase() +{ + *this = QJniArrayBase::fromContainer(std::forward(container)); +} + +namespace QtJniTypes +{ +template struct IsJniArray: std::false_type {}; +template struct IsJniArray> : std::true_type {}; +template struct Traits> { + template = true> + static constexpr auto signature() + { + return CTString("[") + Traits::signature(); + } +}; +template struct Traits> { + template = true> + static constexpr auto signature() + { + return CTString("[") + Traits::signature(); + } +}; +template <> struct Traits { + static constexpr auto signature() + { + return CTString("[B"); + } +}; +} + +QT_END_NAMESPACE + +#endif + +#endif // QJNIARRAY_H diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 6fe646d50f..0ba7209b85 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -37,30 +37,9 @@ class Q_CORE_EXPORT QJniObject bool checkAndClearExceptions() { return env.checkAndClearExceptions(); } template - auto convertToJni(T &&value) - { - using Type = q20::remove_cvref_t; - if constexpr (std::is_same_v) { - return newLocalRef(QJniObject::fromString(value)); - } else if constexpr (std::is_base_of_v) { - return value.object(); - } else { - return static_cast(value); - } - } + auto convertToJni(T &&value); template - auto convertFromJni(QJniObject &&object) - { - using Type = q20::remove_cvref_t; - if constexpr (std::is_same_v) { - return object.toString(); - } else if constexpr (std::is_base_of_v - && !std::is_same_v) { - return T{std::move(object)}; - } else { - return std::move(object); - } - } + auto convertFromJni(QJniObject &&object); }; public: QJniObject(); @@ -779,6 +758,58 @@ inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2) return !obj1.isSameObject(obj2); } +// This cannot be included earlier as QJniArray is a QJniObject subclass, but it +// must be included so that we can implement QJniObject::LocalFrame conversion. +#include + +template +template +auto QJniObject::LocalFrame::convertToJni(T &&value) +{ + using Type = q20::remove_cvref_t; + if constexpr (std::is_same_v) { + return newLocalRef(QJniObject::fromString(value)); + } else if constexpr (QtJniTypes::IsJniArray::value) { + return value.arrayObject(); + } else if constexpr (QJniArrayBase::CanConvert) { + using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward(value))); + using ArrayType = decltype(std::declval().arrayObject()); + return newLocalRef(QJniArrayBase::fromContainer(std::forward(value)).template object()); + } else if constexpr (std::is_base_of_v) { + return value.object(); + } else { + return static_cast(value); + } +} + +template +template +auto QJniObject::LocalFrame::convertFromJni(QJniObject &&object) +{ + using Type = q20::remove_cvref_t; + if constexpr (std::is_same_v) { + return object.toString(); + } else if constexpr (QtJniTypes::IsJniArray::value) { + return T{object}; + } else if constexpr (QJniArrayBase::CanConvert) { + // if we were to create a QJniArray from Type... + using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval())); + // then that QJniArray would have elements of type + using ElementType = typename QJniArrayType::Type; + // construct a QJniArray from a jobject pointer of that type + return QJniArray(object.template object()).asContainer(); + } else if constexpr (std::is_array_v) { + using ElementType = std::remove_extent_t; + return QJniArray{object}; + } else if constexpr (std::is_base_of_v + && !std::is_same_v) { + return T{std::move(object)}; + } else { + return std::move(object); + } +} + + QT_END_NAMESPACE #endif diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt index ce0e82bae2..130fc080b6 100644 --- a/tests/auto/corelib/kernel/CMakeLists.txt +++ b/tests/auto/corelib/kernel/CMakeLists.txt @@ -49,4 +49,5 @@ if(ANDROID) add_subdirectory(qjnienvironment) add_subdirectory(qjniobject) add_subdirectory(qjnitypes) + add_subdirectory(qjniarray) endif() diff --git a/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt b/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt new file mode 100644 index 0000000000..308b2c6427 --- /dev/null +++ b/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjnitypes LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qjniarray + SOURCES + tst_qjniarray.cpp +) diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp new file mode 100644 index 0000000000..fa0da3ca66 --- /dev/null +++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include + +#include +#include + +using namespace Qt::StringLiterals; + +class tst_QJniArray : public QObject +{ + Q_OBJECT + +public: + tst_QJniArray() = default; +}; + +using namespace QtJniTypes; + +// fake type so that we can compile-time test and assert correct type mappings +Q_DECLARE_JNI_CLASS(List, "qt/test/List"); + +// verify that we get the return type we expect for the specified return type +#define VERIFY_RETURN_FOR_TYPE(In, Out) \ +static_assert(std::is_same_v("toByteArray")), Out>) + +VERIFY_RETURN_FOR_TYPE(jobjectArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jobject[], QJniArray); +//VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jbyteArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jbyte[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QByteArray, QByteArray); + +VERIFY_RETURN_FOR_TYPE(jbooleanArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jboolean[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jcharArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jchar[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jshortArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jshort[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jintArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jint[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jlongArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jlong[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +// VERIFY_RETURN_FOR_TYPE(QList, QList); // assumes long is 64bit + +VERIFY_RETURN_FOR_TYPE(jfloatArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jfloat[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(jdoubleArray, QJniObject); +VERIFY_RETURN_FOR_TYPE(jdouble[], QJniArray); +VERIFY_RETURN_FOR_TYPE(QList, QList); +VERIFY_RETURN_FOR_TYPE(QList, QList); + +VERIFY_RETURN_FOR_TYPE(QString, QString); + +QTEST_MAIN(tst_QJniArray) + +#include "tst_qjniarray.moc" diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java index d1b4ea480e..b56447198f 100644 --- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -135,44 +135,143 @@ public class QtJniObjectTestClass public static Object[] staticObjectArrayMethod() { Object[] array = { new Object(), new Object(), new Object() }; return array; } public Object[] objectArrayMethod() { return staticObjectArrayMethod(); } + public static Object[] staticReverseObjectArray(Object[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + Object old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public Object[] reverseObjectArray(Object[] array) + { return staticReverseObjectArray(array); } // -------------------------------------------------------------------------------------------- public static boolean[] staticBooleanArrayMethod() { boolean[] array = { true, true, true }; return array; } public boolean[] booleanArrayMethod() { return staticBooleanArrayMethod(); } + public static boolean[] staticReverseBooleanArray(boolean[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + boolean old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public boolean[] reverseBooleanArray(boolean[] array) + { return staticReverseBooleanArray(array); } // -------------------------------------------------------------------------------------------- public static byte[] staticByteArrayMethod() { byte[] array = { 'a', 'b', 'c' }; return array; } public byte[] byteArrayMethod() { return staticByteArrayMethod(); } + public static byte[] staticReverseByteArray(byte[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + byte old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public byte[] reverseByteArray(byte[] array) + { return staticReverseByteArray(array); } // -------------------------------------------------------------------------------------------- public static char[] staticCharArrayMethod() { char[] array = { 'a', 'b', 'c' }; return array; } public char[] charArrayMethod() { return staticCharArrayMethod(); } + public static char[] staticReverseCharArray(char[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + char old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public char[] reverseCharArray(char[] array) + { return staticReverseCharArray(array); } // -------------------------------------------------------------------------------------------- public static short[] staticShortArrayMethod() { short[] array = { 3, 2, 1 }; return array; } public short[] shortArrayMethod() { return staticShortArrayMethod(); } + public static short[] staticReverseShortArray(short[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + short old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public short[] reverseShortArray(short[] array) + { return staticReverseShortArray(array); } // -------------------------------------------------------------------------------------------- public static int[] staticIntArrayMethod() { int[] array = { 3, 2, 1 }; return array; } public int[] intArrayMethod() { return staticIntArrayMethod(); } + public static int[] staticReverseIntArray(int[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + int old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public int[] reverseIntArray(int[] array) + { return staticReverseIntArray(array); } // -------------------------------------------------------------------------------------------- public static long[] staticLongArrayMethod() { long[] array = { 3, 2, 1 }; return array; } public long[] longArrayMethod() { return staticLongArrayMethod(); } + public static long[] staticReverseLongArray(long[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + long old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public long[] reverseLongArray(long[] array) + { return staticReverseLongArray(array); } // -------------------------------------------------------------------------------------------- public static float[] staticFloatArrayMethod() { float[] array = { 1.0f, 2.0f, 3.0f }; return array; } public float[] floatArrayMethod() { return staticFloatArrayMethod(); } + public static float[] staticReverseFloatArray(float[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + float old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public float[] reverseFloatArray(float[] array) + { return staticReverseFloatArray(array); } // -------------------------------------------------------------------------------------------- public static double[] staticDoubleArrayMethod() { double[] array = { 3.0, 2.0, 1.0 }; return array; } public double[] doubleArrayMethod() { return staticDoubleArrayMethod(); } + public static double[] staticReverseDoubleArray(double[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + double old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public double[] reverseDoubleArray(double[] array) + { return staticReverseDoubleArray(array); } // -------------------------------------------------------------------------------------------- native public int callbackWithObject(QtJniObjectTestClass that); diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 47c3a707f0..751e5a84d8 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1495,6 +1495,18 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticObjectArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + + QJniArray newArray(QList{QJniObject::fromString(u"one"_s), + QJniObject::fromString(u"two"_s), + QJniObject::fromString(u"three"_s)}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseObjectArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.size(), 3); + QCOMPARE(QJniObject(reverse.at(0)).toString(), u"three"_s); + QCOMPARE(QJniObject(reverse.at(1)).toString(), u"two"_s); + QCOMPARE(QJniObject(reverse.at(2)).toString(), u"one"_s); } { @@ -1503,6 +1515,18 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("objectArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + + QJniArray newArray(QList{QJniObject::fromString(u"one"_s), + QJniObject::fromString(u"two"_s), + QJniObject::fromString(u"three"_s)}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseObjectArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.size(), 3); + QCOMPARE(QJniObject(reverse.at(0)).toString(), u"three"_s); + QCOMPARE(QJniObject(reverse.at(1)).toString(), u"two"_s); + QCOMPARE(QJniObject(reverse.at(2)).toString(), u"one"_s); } // jbooleanArray ------------------------------------------------------------------------------ @@ -1513,6 +1537,14 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticBooleanArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{true, true, true})); + + QJniArray newArray(QList{true, false, false}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseBooleanArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{false, false, true})); } { @@ -1521,6 +1553,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("booleanArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{true, true, true})); + + QJniArray newArray(QList{true, false, false}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseBooleanArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{false, false, true})); } // jbyteArray --------------------------------------------------------------------------------- @@ -1531,6 +1571,18 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticByteArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), "abc"); + + QJniArray newArray(QByteArray{"cba"}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseByteArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), "abc"); + + const QByteArray reverse2 = TestClass::callStaticMethod("staticReverseByteArray", + QByteArray("abc")); + QCOMPARE(reverse2, "cba"); } { @@ -1539,6 +1591,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("byteArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), "abc"); + + QJniArray newArray = QJniArrayBase::fromContainer(QByteArray{"cba"}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseByteArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), "abc"); } // jcharArray --------------------------------------------------------------------------------- @@ -1549,6 +1609,18 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticCharArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{u'a', u'b', u'c'})); + + QJniArray newArray(QList{u'c', u'b', u'a'}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseCharArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{u'a', u'b', u'c'})); + + const QList reverse2 = TestClass::callStaticMethod>("staticReverseCharArray", + (QList{u'c', u'b', u'a'})); + QCOMPARE(reverse2, (QList{u'a', u'b', u'c'})); } { @@ -1557,6 +1629,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("charArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{u'a', u'b', u'c'})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{u'c', u'b', u'a'}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseCharArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{u'a', u'b', u'c'})); } // jshortArray -------------------------------------------------------------------------------- @@ -1567,6 +1647,18 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticShortArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray(QList{3, 2, 1}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseShortArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); + + const QList reverse2 = TestClass::callStaticMethod>("staticReverseShortArray", + (QList{1, 2, 3})); + QCOMPARE(reverse2, (QList{3, 2, 1})); } { @@ -1575,6 +1667,15 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("shortArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{3, 2, 1}); + static_assert(std::is_same_v); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseShortArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); } // jintArray ---------------------------------------------------------------------------------- @@ -1585,6 +1686,14 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticIntArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray(QList{3, 2, 1}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseIntArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); } { @@ -1593,6 +1702,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("intArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{3, 2, 1}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseIntArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); } // jlongArray --------------------------------------------------------------------------------- @@ -1603,6 +1720,14 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticLongArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray(QList{3, 2, 1}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseLongArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); } { @@ -1611,6 +1736,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("longArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3, 2, 1})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{3, 2, 1}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseLongArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1, 2, 3})); } // jfloatArray -------------------------------------------------------------------------------- @@ -1621,6 +1754,14 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticFloatArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{1.0f, 2.0f, 3.0f})); + + QJniArray newArray(QList{3.0f, 2.0f, 1.0f}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseFloatArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1.0f, 2.0f, 3.0f})); } { @@ -1629,6 +1770,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("floatArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{1.0f, 2.0f, 3.0f})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{3.0f, 2.0f, 1.0f}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseFloatArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1.0f, 2.0f, 3.0f})); } // jdoubleArray ------------------------------------------------------------------------------- @@ -1639,6 +1788,14 @@ void tst_QJniObject::templateApiCheck() const auto array = TestClass::callStaticMethod("staticDoubleArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3.0, 2.0, 1.0})); + + QJniArray newArray(QList{3.0, 2.0, 1.0}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod("staticReverseDoubleArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1.0, 2.0, 3.0})); } { @@ -1647,6 +1804,14 @@ void tst_QJniObject::templateApiCheck() const auto array = testClass.callMethod("doubleArrayMethod"); QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.asContainer(), (QList{3.0, 2.0, 1.0})); + + QJniArray newArray = QJniArrayBase::fromContainer(QList{3.0, 2.0, 1.0}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod("reverseDoubleArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.asContainer(), (QList{1.0, 2.0, 3.0})); } } diff --git a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp index a499b6f2d2..f9d9a4c13c 100644 --- a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp +++ b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp @@ -4,6 +4,7 @@ #include #include +#include using namespace Qt::StringLiterals; @@ -59,8 +60,7 @@ static_assert(!(QtJniTypes::Traits::signature() == "X")); Q_DECLARE_JNI_CLASS(JavaType, "org/qtproject/qt/JavaType"); static_assert(QtJniTypes::Traits::signature() == "Lorg/qtproject/qt/JavaType;"); -Q_DECLARE_JNI_TYPE(ArrayType, "[Lorg/qtproject/qt/ArrayType;") -static_assert(QtJniTypes::Traits::signature() == "[Lorg/qtproject/qt/ArrayType;"); +static_assert(QtJniTypes::Traits::signature() == "[Lorg/qtproject/qt/JavaType;"); Q_DECLARE_JNI_CLASS(String, "java/lang/String"); static_assert(QtJniTypes::Traits::className() == "java/lang/String"); @@ -119,6 +119,10 @@ static_assert(!QtJniTypes::CTString("ABCDE").startsWith("9AB")); static_assert(QtJniTypes::CTString("ABCDE").startsWith('A')); static_assert(!QtJniTypes::CTString("ABCDE").startsWith('B')); +static_assert(QtJniTypes::Traits>::signature() == "[Ljava/lang/Object;"); +static_assert(QtJniTypes::Traits>::signature() == "[B"); +static_assert(QtJniTypes::isObjectType>()); + static_assert(QtJniTypes::CTString("ABCDE").endsWith("CDE")); static_assert(QtJniTypes::CTString("ABCDE").endsWith("E")); static_assert(QtJniTypes::CTString("ABCDE").endsWith("ABCDE")); -- cgit v1.2.3