Generate JNI signature strings at compile time

Introduce an internal QtJniTypes namespace with types that allow us to
concatenate string literals at compile time. This makes it possible to
generate arbitrary strings based on types, which we can then use as
signatures to JNI method calls.

Move some of the private members of QJniObject into the QtJniTypes
namespace for consistency, and to allow further template specialization
by user code to make other types and their JNI signature string known.
Remove the "Jni" prefix from names.

Use the compile-time generated string in QJniObject methods that created
the signature string at runtime, which involved a temporary memory
allocation.

Treat 'void' as a primitive type (with signature string 'V'), and
remove redundant template specializations.

Add a test case to verify the the strings are constructed correctly
at compile time.

Change-Id: I5e3895a97f7dc1b86961f7a7855b899d9203037d
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Volker Hilsheimer 2022-04-20 17:07:42 +02:00
parent dffca8bb0e
commit a085a14d76
6 changed files with 495 additions and 164 deletions

View File

@ -949,6 +949,7 @@ qt_internal_extend_target(Core CONDITION ANDROID
SOURCES
io/qstandardpaths_android.cpp
io/qstorageinfo_unix.cpp
kernel/qjnitypes.h
kernel/qjnienvironment.cpp kernel/qjnienvironment.h
kernel/qjniobject.cpp kernel/qjniobject.h
kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -45,6 +45,7 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE
@ -64,7 +65,7 @@ public:
jobject object() const;
template <typename T> T object() const
{
assertJniObjectType<T>();
QtJniTypes::assertObjectType<T>();
return static_cast<T>(javaObject());
}
@ -74,7 +75,7 @@ public:
template <typename T>
T callMethod(const char *methodName, const char *signature, ...) const
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
@ -106,23 +107,17 @@ public:
template <typename T>
T callMethod(const char *methodName) const
{
assertJniPrimitiveType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callMethod<T>(methodName, QByteArray(signature).prepend("()").constData());
}
template <>
void callMethod<void>(const char *methodName) const
{
callMethod<void>(methodName, "()V");
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callMethod<T>(methodName, signature);
}
template <typename T>
QJniObject callObjectMethod(const char *methodName) const
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callObjectMethod(methodName, QByteArray(signature).prepend("()").constData());
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callObjectMethod(methodName, signature);
}
QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
@ -131,7 +126,7 @@ public:
static T callStaticMethod(const char *className, const char *methodName,
const char *signature, ...)
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
@ -174,21 +169,15 @@ public:
template <typename T>
static T callStaticMethod(const char *className, const char *methodName)
{
assertJniPrimitiveType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callStaticMethod<T>(className, methodName, QByteArray(signature).prepend("()").constData());
}
template <>
void callStaticMethod<void>(const char *className, const char *methodName)
{
callStaticMethod<void>(className, methodName, "()V");
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticMethod<T>(className, methodName, signature);
}
template <typename T>
static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
if (clazz) {
@ -225,7 +214,7 @@ public:
template <typename T>
static T callStaticMethod(jclass clazz, jmethodID methodId, ...)
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
if (clazz && methodId) {
@ -254,23 +243,17 @@ public:
template <typename T> static T callStaticMethod(jclass clazz, const char *methodName)
{
assertJniPrimitiveType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callStaticMethod<T>(clazz, methodName, QByteArray(signature).prepend("()").constData());
}
template <>
void callStaticMethod<void>(jclass clazz, const char *methodName)
{
callStaticMethod<void>(clazz, methodName, "()V");
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticMethod<T>(clazz, methodName, signature);
}
template <typename T>
static QJniObject callStaticObjectMethod(const char *className, const char *methodName)
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callStaticObjectMethod(className, methodName, QByteArray(signature).prepend("()").constData());
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticObjectMethod(className, methodName, signature);
}
static QJniObject callStaticObjectMethod(const char *className, const char *methodName,
@ -279,9 +262,9 @@ public:
template <typename T>
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName)
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
return callStaticObjectMethod(clazz, methodName, QByteArray(signature).prepend("()").constData());
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticObjectMethod(clazz, methodName, signature);
}
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName,
@ -291,10 +274,10 @@ public:
template <typename T> T getField(const char *fieldName) const
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
if (id) {
getFieldForType<T>(env.jniEnv(), res, object(), id);
@ -307,13 +290,13 @@ public:
template <typename T>
static T getStaticField(const char *className, const char *fieldName)
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (!clazz)
return 0;
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(env.jniEnv(), clazz,
QJniObject::toBinaryEncClassName(className),
fieldName,
@ -331,10 +314,10 @@ public:
template <typename T>
static T getStaticField(jclass clazz, const char *fieldName)
{
assertJniPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (id) {
getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
@ -347,8 +330,8 @@ public:
template <typename T>
QJniObject getObjectField(const char *fieldName) const
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getObjectField(fieldName, signature);
}
@ -357,8 +340,8 @@ public:
template <typename T>
static QJniObject getStaticObjectField(const char *className, const char *fieldName)
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(className, fieldName, signature);
}
@ -369,8 +352,8 @@ public:
template <typename T>
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName)
{
assertJniObjectType<T>();
constexpr const char *signature = getTypeSignature<T>();
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(clazz, fieldName, signature);
}
@ -379,9 +362,9 @@ public:
template <typename T> void setField(const char *fieldName, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
if (id) {
setFieldForType<T>(env.jniEnv(), object(), id, value);
@ -392,7 +375,7 @@ public:
template <typename T>
void setField(const char *fieldName, const char *signature, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
if (id) {
@ -404,13 +387,13 @@ public:
template <typename T>
static void setStaticField(const char *className, const char *fieldName, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (!clazz)
return;
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
signature, true);
if (!id)
@ -424,7 +407,7 @@ public:
static void setStaticField(const char *className, const char *fieldName,
const char *signature, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
@ -443,7 +426,7 @@ public:
static void setStaticField(jclass clazz, const char *fieldName,
const char *signature, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
@ -456,9 +439,9 @@ public:
template <typename T>
static void setStaticField(jclass clazz, const char *fieldName, T value)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
QJniEnvironment env;
constexpr const char *signature = getTypeSignature<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (id) {
setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
@ -477,7 +460,7 @@ public:
template <typename T> QJniObject &operator=(T obj)
{
assertJniType<T>();
QtJniTypes::assertType<T>();
assign(static_cast<T>(obj));
return *this;
}
@ -525,100 +508,6 @@ private:
friend bool operator==(const QJniObject &, const QJniObject &);
friend bool operator!=(const QJniObject&, const QJniObject&);
template<bool flag = false>
static void staticAssertTypeMismatch()
{
static_assert(flag, "The used type is not supported by this template call. "
"Use a JNI based type instead.");
}
template<typename T>
static constexpr bool isJniPrimitiveType()
{
if constexpr(!std::is_same<T, jboolean>::value
&& !std::is_same<T, jbyte>::value
&& !std::is_same<T, jchar>::value
&& !std::is_same<T, jshort>::value
&& !std::is_same<T, jint>::value
&& !std::is_same<T, jlong>::value
&& !std::is_same<T, jfloat>::value
&& !std::is_same<T, jdouble>::value) {
return false;
}
return true;
}
template<typename T>
static constexpr void assertJniPrimitiveType()
{
if constexpr(!isJniPrimitiveType<T>())
staticAssertTypeMismatch();
}
template<typename T>
static constexpr void assertJniObjectType()
{
if constexpr(!std::is_convertible<T, jobject>::value)
staticAssertTypeMismatch();
}
template<typename T>
static constexpr void assertJniType()
{
if constexpr(!isJniPrimitiveType<T>() && !std::is_convertible<T, jobject>::value)
staticAssertTypeMismatch();
}
template<typename T>
static constexpr const char* getTypeSignature()
{
if constexpr(std::is_same<T, jobject>::value)
return "Ljava/lang/Object;";
else if constexpr(std::is_same<T, jclass>::value)
return "Ljava/lang/Class;";
else if constexpr(std::is_same<T, jstring>::value)
return "Ljava/lang/String;";
else if constexpr(std::is_same<T, jobjectArray>::value)
return "[Ljava/lang/Object;";
else if constexpr(std::is_same<T, jthrowable>::value)
return "Ljava/lang/Throwable;";
else if constexpr(std::is_same<T, jbooleanArray>::value)
return "[Z";
else if constexpr(std::is_same<T, jbyteArray>::value)
return "[B";
else if constexpr(std::is_same<T, jshortArray>::value)
return "[S";
else if constexpr(std::is_same<T, jintArray>::value)
return "[I";
else if constexpr(std::is_same<T, jlongArray>::value)
return "[J";
else if constexpr(std::is_same<T, jfloatArray>::value)
return "[F";
else if constexpr(std::is_same<T, jdoubleArray>::value)
return "[D";
else if constexpr(std::is_same<T, jcharArray>::value)
return "[C";
else if constexpr(std::is_same<T, jboolean>::value)
return "Z";
else if constexpr(std::is_same<T, jbyte>::value)
return "B";
else if constexpr(std::is_same<T, jchar>::value)
return "C";
else if constexpr(std::is_same<T, jshort>::value)
return "S";
else if constexpr(std::is_same<T, jint>::value)
return "I";
else if constexpr(std::is_same<T, jlong>::value)
return "J";
else if constexpr(std::is_same<T, jfloat>::value)
return "F";
else if constexpr(std::is_same<T, jdouble>::value)
return "D";
else
staticAssertTypeMismatch();
}
template<typename T>
static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
jmethodID id, va_list args)
@ -640,7 +529,7 @@ private:
else if constexpr(std::is_same<T, jdouble>::value)
res = env->CallDoubleMethodV(obj, id, args);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
template<typename T>
@ -664,7 +553,7 @@ private:
else if constexpr(std::is_same<T, jdouble>::value)
res = env->CallStaticDoubleMethodV(clazz, id, args);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
template<typename T>
@ -688,7 +577,7 @@ private:
else if constexpr(std::is_same<T, jdouble>::value)
res = env->GetDoubleField(obj, id);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
template<typename T>
@ -712,7 +601,7 @@ private:
else if constexpr(std::is_same<T, jdouble>::value)
res = env->GetStaticDoubleField(clazz, id);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
template<typename T>
@ -738,7 +627,7 @@ private:
else if constexpr(std::is_convertible<T, jobject>::value)
env->SetObjectField(obj, id, value);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
template<typename T>
@ -764,7 +653,7 @@ private:
else if constexpr(std::is_convertible<T, jobject>::value)
env->SetStaticObjectField(clazz, id, value);
else
staticAssertTypeMismatch();
QtJniTypes::staticAssertTypeMismatch();
}
friend QJniObjectPrivate;

View File

@ -0,0 +1,318 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QJNITYPES_H
#define QJNITYPES_H
#include <QtCore/qglobal.h>
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
QT_BEGIN_NAMESPACE
namespace QtJniTypes
{
// a constexpr type for string literals of any character width, aware of the length
// of the string.
template<size_t N_WITH_NULL, typename BaseType = char>
struct String
{
BaseType m_data[N_WITH_NULL] = {};
constexpr String() noexcept {}
// Can be instantiated (only) with a string literal
constexpr explicit String(const BaseType (&data)[N_WITH_NULL]) noexcept
{
for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
m_data[i] = data[i];
}
constexpr BaseType at(size_t i) const { return m_data[i]; }
constexpr BaseType operator[](size_t i) const { return at(i); }
static constexpr size_t size() noexcept { return N_WITH_NULL; }
constexpr operator const BaseType *() const noexcept { return m_data; }
constexpr const BaseType *data() const noexcept { return m_data; }
template<size_t N2_WITH_NULL>
constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
{
if constexpr (N2_WITH_NULL > N_WITH_NULL) {
return false;
} else {
for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
if (m_data[i] != lit[i])
return false;
}
}
return true;
}
constexpr bool startsWith(BaseType c) const noexcept
{
return N_WITH_NULL > 1 && m_data[0] == c;
}
template<size_t N2_WITH_NULL>
constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
{
if constexpr (N2_WITH_NULL > N_WITH_NULL) {
return false;
} else {
for (size_t i = 0; i < N2_WITH_NULL; ++i) {
if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
return false;
}
}
return true;
}
constexpr bool endsWith(BaseType c) const noexcept
{
return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
const String<N2_WITH_NULL> &rhs) noexcept
{
if constexpr (N_WITH_NULL != N2_WITH_NULL) {
return false;
} else {
for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
if (lhs.at(i) != rhs.at(i))
return false;
}
}
return true;
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
const String<N2_WITH_NULL> &rhs) noexcept
{
return !operator==(lhs, rhs);
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
const BaseType (&rhs)[N2_WITH_NULL]) noexcept
{
return operator==(lhs, String<N2_WITH_NULL>(rhs));
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
const String<N_WITH_NULL> &rhs) noexcept
{
return operator==(String<N2_WITH_NULL>(lhs), rhs);
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
const BaseType (&rhs)[N2_WITH_NULL]) noexcept
{
return operator!=(lhs, String<N2_WITH_NULL>(rhs));
}
template<size_t N2_WITH_NULL>
friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
const String<N_WITH_NULL> &rhs) noexcept
{
return operator!=(String<N2_WITH_NULL>(lhs), rhs);
}
template<size_t N2_WITH_NULL>
friend inline constexpr auto operator+(const String<N_WITH_NULL> &lhs,
const String<N2_WITH_NULL> &rhs) noexcept
{
char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
data[i] = lhs[i];
for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
data[N_WITH_NULL - 1 + i] = rhs[i];
return String<N_WITH_NULL + N2_WITH_NULL - 1>(data);
}
};
// Helper types that allow us to disable variadic overloads that would conflict
// with overloads that take a const char*.
template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
template<> struct IsStringType<const char*, 0> : std::true_type {};
template<size_t N> struct IsStringType<String<N>> : std::true_type {};
template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
template<bool flag = false>
static void staticAssertTypeMismatch()
{
static_assert(flag, "The used type is not supported by this template call. "
"Use a JNI based type instead.");
}
template<typename T>
constexpr auto typeSignature()
{
if constexpr(std::is_same<T, jobject>::value)
return String("Ljava/lang/Object;");
else if constexpr(std::is_same<T, jclass>::value)
return String("Ljava/lang/Class;");
else if constexpr(std::is_same<T, jstring>::value)
return String("Ljava/lang/String;");
else if constexpr(std::is_same<T, jobjectArray>::value)
return String("[Ljava/lang/Object;");
else if constexpr(std::is_same<T, jthrowable>::value)
return String("Ljava/lang/Throwable;");
else if constexpr(std::is_same<T, jbooleanArray>::value)
return String("[Z");
else if constexpr(std::is_same<T, jbyteArray>::value)
return String("[B");
else if constexpr(std::is_same<T, jshortArray>::value)
return String("[S");
else if constexpr(std::is_same<T, jintArray>::value)
return String("[I");
else if constexpr(std::is_same<T, jlongArray>::value)
return String("[J");
else if constexpr(std::is_same<T, jfloatArray>::value)
return String("[F");
else if constexpr(std::is_same<T, jdoubleArray>::value)
return String("[D");
else if constexpr(std::is_same<T, jcharArray>::value)
return String("[C");
else if constexpr(std::is_same<T, jboolean>::value)
return String("Z");
else if constexpr(std::is_same<T, bool>::value)
return String("Z");
else if constexpr(std::is_same<T, jbyte>::value)
return String("B");
else if constexpr(std::is_same<T, jchar>::value)
return String("C");
else if constexpr(std::is_same<T, char>::value)
return String("C");
else if constexpr(std::is_same<T, jshort>::value)
return String("S");
else if constexpr(std::is_same<T, short>::value)
return String("S");
else if constexpr(std::is_same<T, jint>::value)
return String("I");
else if constexpr(std::is_same<T, int>::value)
return String("I");
else if constexpr(std::is_same<T, uint>::value)
return String("I");
else if constexpr(std::is_same<T, jlong>::value)
return String("J");
else if constexpr(std::is_same<T, long>::value)
return String("J");
else if constexpr(std::is_same<T, jfloat>::value)
return String("F");
else if constexpr(std::is_same<T, float>::value)
return String("F");
else if constexpr(std::is_same<T, jdouble>::value)
return String("D");
else if constexpr(std::is_same<T, double>::value)
return String("D");
else if constexpr(std::is_same<T, void>::value)
return String("V");
else if constexpr(IsStringType<T>::value)
static_assert(!IsStringType<T>::value, "Don't use a literal type, call data!");
else
staticAssertTypeMismatch();
}
template<typename T>
static constexpr bool isPrimitiveType()
{
return typeSignature<T>().size() == 2;
}
template<typename T>
static constexpr bool isObjectType()
{
if constexpr(std::is_convertible<T, jobject>::value) {
return true;
} else {
constexpr auto signature = typeSignature<T>();
return signature.startsWith('L') && signature.endsWith(';');
}
}
template<typename T>
static constexpr void assertPrimitiveType()
{
static_assert(isPrimitiveType<T>(), "Type needs to be a primitive JNI type!");
}
template<typename T>
static constexpr void assertObjectType()
{
static_assert(isObjectType<T>(),
"Type needs to be a JNI object type (convertible to jobject, or with "
"an object type signature registered)!");
}
template<typename T>
static constexpr void assertType()
{
static_assert(isPrimitiveType<T>() || isObjectType<T>(),
"Type needs to be a JNI type!");
}
template<typename R, typename ...Args>
static constexpr auto methodSignature()
{
return (String("(") +
... + typeSignature<std::decay_t<Args>>())
+ String(")")
+ typeSignature<R>();
}
template<typename T>
static constexpr auto fieldSignature()
{
return QtJniTypes::typeSignature<T>();
}
template<typename ...Args>
static constexpr auto constructorSignature()
{
return methodSignature<void, Args...>();
}
} // namespace QtJniTypes
QT_END_NAMESPACE
#endif
#endif // QJNITYPES_H

View File

@ -49,4 +49,5 @@ endif()
if(ANDROID)
add_subdirectory(qjnienvironment)
add_subdirectory(qjniobject)
add_subdirectory(qjnitypes)
endif()

View File

@ -0,0 +1,4 @@
qt_internal_add_test(tst_qjnitypes
SOURCES
tst_qjnitypes.cpp
)

View File

@ -0,0 +1,118 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest>
#include <QtCore/qjnitypes.h>
class tst_QJniTypes : public QObject
{
Q_OBJECT
public:
tst_QJniTypes() = default;
private slots:
void initTestCase();
};
struct QtJavaWrapper {};
template<>
constexpr auto QtJniTypes::typeSignature<QtJavaWrapper>()
{
return QtJniTypes::String("Lorg/qtproject/qt/android/QtJavaWrapper;");
}
template<>
constexpr auto QtJniTypes::typeSignature<QJniObject>()
{
return QtJniTypes::String("Ljava/lang/Object;");
}
struct QtCustomJniObject : QJniObject {};
template<>
constexpr auto QtJniTypes::typeSignature<QtCustomJniObject>()
{
return QtJniTypes::String("Lorg/qtproject/qt/android/QtCustomJniObject;");
}
static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() == "Lorg/qtproject/qt/android/QtJavaWrapper;");
static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() != "Ljava/lang/Object;");
static_assert(!(QtJniTypes::typeSignature<QtJavaWrapper>() == "X"));
static_assert(QtJniTypes::fieldSignature<jint>() == "I");
static_assert(QtJniTypes::fieldSignature<jint>() != "X");
static_assert(QtJniTypes::fieldSignature<jint>() != "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<jlong>() == "J");
static_assert(QtJniTypes::fieldSignature<jstring>() == "Ljava/lang/String;");
static_assert(QtJniTypes::fieldSignature<jobject>() == "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<jobjectArray>() == "[Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<QJniObject>() == "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<QtJavaWrapper>() == "Lorg/qtproject/qt/android/QtJavaWrapper;");
static_assert(QtJniTypes::fieldSignature<QtCustomJniObject>() == "Lorg/qtproject/qt/android/QtCustomJniObject;");
static_assert(QtJniTypes::methodSignature<void>() == "()V");
static_assert(QtJniTypes::methodSignature<void>() != "()X");
static_assert(QtJniTypes::methodSignature<void, jint>() == "(I)V");
static_assert(QtJniTypes::methodSignature<void, jint, jstring>() == "(ILjava/lang/String;)V");
static_assert(QtJniTypes::methodSignature<jlong, jint, jclass>() == "(ILjava/lang/Class;)J");
static_assert(QtJniTypes::methodSignature<jobject, jint, jstring>() == "(ILjava/lang/String;)Ljava/lang/Object;");
static_assert(QtJniTypes::isPrimitiveType<jint>());
static_assert(QtJniTypes::isPrimitiveType<void>());
static_assert(!QtJniTypes::isPrimitiveType<jobject>());
static_assert(!QtJniTypes::isPrimitiveType<QtCustomJniObject>());
static_assert(!QtJniTypes::isObjectType<jint>());
static_assert(!QtJniTypes::isObjectType<void>());
static_assert(QtJniTypes::isObjectType<jobject>());
static_assert(QtJniTypes::isObjectType<QtCustomJniObject>());
static_assert(QtJniTypes::String("ABCDE").startsWith("ABC"));
static_assert(QtJniTypes::String("ABCDE").startsWith("A"));
static_assert(QtJniTypes::String("ABCDE").startsWith("ABCDE"));
static_assert(!QtJniTypes::String("ABCDE").startsWith("ABCDEF"));
static_assert(!QtJniTypes::String("ABCDE").startsWith("9AB"));
static_assert(QtJniTypes::String("ABCDE").startsWith('A'));
static_assert(!QtJniTypes::String("ABCDE").startsWith('B'));
static_assert(QtJniTypes::String("ABCDE").endsWith("CDE"));
static_assert(QtJniTypes::String("ABCDE").endsWith("E"));
static_assert(QtJniTypes::String("ABCDE").endsWith("ABCDE"));
static_assert(!QtJniTypes::String("ABCDE").endsWith("DEF"));
static_assert(!QtJniTypes::String("ABCDE").endsWith("ABCDEF"));
static_assert(QtJniTypes::String("ABCDE").endsWith('E'));
static_assert(!QtJniTypes::String("ABCDE").endsWith('F'));
void tst_QJniTypes::initTestCase()
{
}
QTEST_MAIN(tst_QJniTypes)
#include "tst_qjnitypes.moc"