diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp index 9766ff4d3b..3eab31bbc2 100644 --- a/src/corelib/kernel/qjnienvironment.cpp +++ b/src/corelib/kernel/qjnienvironment.cpp @@ -231,6 +231,16 @@ jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, cons return nullptr; } +/*! + \fn template jmethodId QJniEnvironment::findMethod(jclass clazz, const char *methodName) + \since 6.4 + + Searches for an instance method of a class \a clazz. The method is specified + by its \a methodName, the signature is deduced from the template parameters. + + Returns the method ID or \c nullptr if the method is not found. +*/ + /*! Searches for a static method of a class \a clazz. The method is specified by its \a methodName and \a signature. @@ -265,9 +275,28 @@ jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName return nullptr; } +/*! + \fn template jmethodId QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName) + \since 6.4 + + Searches for an instance method of a class \a clazz. The method is specified + by its \a methodName, the signature is deduced from the template parameters. + + Returns the method ID or \c nullptr if the method is not found. + + \code + QJniEnvironment env; + jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass"); + jmethodID methodId = env.findStaticMethod(javaClass, "staticJavaMethod"); + QJniObject javaMessage = QJniObject::fromString("findStaticMethod example"); + QJniObject::callStaticMethod(javaClass, + methodId, + javaMessage.object()); + \endcode +*/ /*! - Searches for an member field of a class \a clazz. The field is specified + Searches for a member field of a class \a clazz. The field is specified by its \a fieldName and \a signature. Returns the field ID or \c nullptr if the field is not found. @@ -288,6 +317,16 @@ jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const c return nullptr; } +/*! + \fn template jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName) + \since 6.4 + + Searches for a member field of a class \a clazz. The field is specified + by its \a fieldName. The signature of the field is deduced from the template parameter. + + Returns the field ID or \c nullptr if the field is not found. +*/ + /*! Searches for a static field of a class \a clazz. The field is specified by its \a fieldName and \a signature. @@ -310,6 +349,16 @@ jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, c return nullptr; } +/*! + \fn template jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName) + \since 6.4 + + Searches for a static field of a class \a clazz. The field is specified + by its \a fieldName. The signature of the field is deduced from the template parameter. + + Returns the field ID or \c nullptr if the field is not found. +*/ + /*! \fn JavaVM *QJniEnvironment::javaVM() diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h index 16a6413d31..8263d04834 100644 --- a/src/corelib/kernel/qjnienvironment.h +++ b/src/corelib/kernel/qjnienvironment.h @@ -44,6 +44,7 @@ #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include +#include QT_BEGIN_NAMESPACE @@ -60,9 +61,29 @@ public: JNIEnv *jniEnv() const; jclass findClass(const char *className); jmethodID findMethod(jclass clazz, const char *methodName, const char *signature); + template + jmethodID findMethod(jclass clazz, const char *methodName) { + constexpr auto signature = QtJniTypes::methodSignature(); + return findMethod(clazz, methodName, signature.data()); + } jmethodID findStaticMethod(jclass clazz, const char *methodName, const char *signature); + template + jmethodID findStaticMethod(jclass clazz, const char *methodName) { + constexpr auto signature = QtJniTypes::methodSignature(); + return findStaticMethod(clazz, methodName, signature.data()); + } jfieldID findField(jclass clazz, const char *fieldName, const char *signature); + template + jfieldID findField(jclass clazz, const char *fieldName) { + constexpr auto signature = QtJniTypes::fieldSignature(); + return findField(clazz, fieldName, signature.data()); + } jfieldID findStaticField(jclass clazz, const char *fieldName, const char *signature); + template + jfieldID findStaticField(jclass clazz, const char *fieldName) { + constexpr auto signature = QtJniTypes::fieldSignature(); + return findStaticField(clazz, fieldName, signature.data()); + } static JavaVM *javaVM(); bool registerNativeMethods(const char *className, const JNINativeMethod methods[], int size); bool registerNativeMethods(jclass clazz, const JNINativeMethod methods[], int size); diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp index fcc4851c74..e801aa958f 100644 --- a/src/corelib/kernel/qjniobject.cpp +++ b/src/corelib/kernel/qjniobject.cpp @@ -76,55 +76,77 @@ using namespace Qt::StringLiterals; \section1 Method Signatures - For functions that take no arguments, QJniObject provides convenience functions that will use - the correct signature based on the provided template type. For example: + QJniObject provides convenience functions that will use the correct signature based on the + provided template types. For functions that only return and take \l {JNI types}, the + signature can be generate at compile time: \code jint x = QJniObject::callMethod("getSize"); QJniObject::callMethod("touch"); + jint ret = jString1.callMethod("compareToIgnoreCase", jString2.object()); \endcode - In other cases you will need to supply the signature yourself, and it is important that the - signature matches the function you want to call. The signature structure is - \c "(ArgumentsTypes)ReturnType". Array types in the signature must have the \c {[} prefix, - and the fully-qualified \c Object type names must have the \c L prefix and the \c ; suffix. + These functions are variadic templates, and the compiler will deduce the template arguments + from the actual argument types. In many situations, only the return type needs to be provided + explicitly. - The example below demonstrates how to call two different static functions: + For functions that take other argument types, you need to supply the signature yourself. It is + important that the signature matches the function you want to call. The example below + demonstrates how to call different static functions: \code // Java class package org.qtproject.qt; class TestClass { - static String fromNumber(int x) { ... } - static String[] stringArray(String s1, String s2) { ... } + static TestClass create() { ... } + static String fromNumber(int x) { ... } + static String[] stringArray(String s1, String s2) { ... } } \endcode - The signature for the first function is \c {"(I)Ljava/lang/String;"}: + The signature structure is \c "(ArgumentsTypes)ReturnType". Array types in the signature + must have the \c {[} prefix, and the fully-qualified \c Object type names must have the + \c L prefix and the \c ; suffix. The signature for the \c create function is + \c {"()Lorg/qtproject/qt/TestClass;}. The signatures for the second and third functions + are \c {"(I)Ljava/lang/String;"} and + \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}, respectively. + + We can call the \c create() function like this: + + \code + // C++ code + QJniObject testClass = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass", + "create", + "()Lorg/qtproject/qt/TestClass;"); + \endcode + + For the second and third function we can rely on QJniObject's template methods to create + the implicit signature string, but we can also pass the signature string explicitly: \code // C++ code QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass", - "fromNumber" - "(I)Ljava/lang/String;", - 10); + "fromNumber", + "(I)Ljava/lang/String;", 10); \endcode - and the signature for the second function is - \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}: + For the implicit signature creation to work we need to specify the return type explicitly: \code // C++ code QJniObject string1 = QJniObject::fromString("String1"); QJniObject string2 = QJniObject::fromString("String2"); - QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass", + QJniObject stringArray = QJniObject::callStaticObjectMethod( + "org/qtproject/qt/TestClass", "stringArray" - "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" string1.object(), string2.object()); \endcode + Note that while he first template parameter specifies the return type of the Java + function, the method will still return a QJniObject. + \section1 Handling Java Exception After calling Java functions that might throw exceptions, it is important @@ -403,9 +425,12 @@ jmethodID QJniObject::getMethodID(JNIEnv *env, return id; } -void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const +void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const { + va_list args; + va_start(args, id); env->CallVoidMethodV(d->m_jobject, id, args); + va_end(args); } jmethodID QJniObject::getCachedMethodID(JNIEnv *env, @@ -620,6 +645,22 @@ QJniObject::QJniObject(const char *className, const char *signature, ...) } } +/*! + \fn template QJniObject::QJniObject(const char *className, Args &&...args) + \since 6.4 + + Constructs a new JNI object by calling the constructor of \a className with + the arguments \a args. This constructor is only available if all \a args are + known \l {JNI Types}. + + \code + QJniEnvironment env; + char* str = "Hello"; + jstring myJStringArg = env->NewStringUTF(str); + QJniObject myNewJavaString("java/lang/String", myJStringArg); + \endcode +*/ + QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args) : d(new QJniObjectPrivate()) { @@ -671,6 +712,21 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...) } } +/*! + \fn template QJniObject::QJniObject(jclass clazz, Args &&...args) + \since 6.4 + + Constructs a new JNI object from \a clazz by calling the constructor with + the arguments \a args. This constructor is only available if all \a args are + known \l {JNI Types}. + + \code + QJniEnvironment env; + jclass myClazz = env.findClass("org/qtproject/qt/TestClass"); + QJniObject(myClazz, 3); + \endcode +*/ + /*! Constructs a new JNI object by calling the default constructor of \a clazz. @@ -883,10 +939,11 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, } /*! - \fn template T QJniObject::callMethod(const char *methodName, const char *signature, ...) const + \fn template Ret QJniObject::callMethod(const char *methodName, const char *signature, Args &&...args) const + \since 6.4 Calls the object's method \a methodName with \a signature specifying the types of any - subsequent arguments. + subsequent arguments \a args. \code QJniObject myJavaStrin("org/qtproject/qt/TestClass"); @@ -896,21 +953,25 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, */ /*! - \fn template T QJniObject::callMethod(const char *methodName) const + \fn template Ret QJniObject::callMethod(const char *methodName, Args &&...args) const + \since 6.4 - Calls the method \a methodName and returns the value. + Calls the method \a methodName with arguments \a args and returns the value. \code QJniObject myJavaStrin("org/qtproject/qt/TestClass"); jint size = myJavaString.callMethod("length"); \endcode + + The method signature is deduced at compile time from \c Ret and the types of \a args. */ /*! - \fn template T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...) + \fn template Ret QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args) + \since 6.4 Calls the static method \a methodName from class \a className with \a signature - specifying the types of any subsequent arguments. + specifying the types of any subsequent arguments \a args. \code jint a = 2; @@ -920,17 +981,21 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, */ /*! - \fn template T QJniObject::callStaticMethod(const char *className, const char *methodName) + \fn template Ret QJniObject::callStaticMethod(const char *className, const char *methodName, Args &&...args) + \since 6.4 - Calls the static method \a methodName on class \a className and returns the value. + Calls the static method \a methodName on class \a className with arguments \a args, + and returns the value of type \c Ret. \code jint value = QJniObject::callStaticMethod("MyClass", "staticMethod"); \endcode + + The method signature is deduced at compile time from \c Ret and the types of \a args. */ /*! - \fn template T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...) + \fn template Ret QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args) Calls the static method \a methodName from \a clazz with \a signature specifying the types of any subsequent arguments. @@ -945,7 +1010,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, */ /*! - \fn template T QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, ...) + \fn template Ret QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) + \since 6.4 Calls the static method identified by \a methodId from the class \a clazz with any subsequent arguments. Useful when \a clazz and \a methodId are @@ -964,7 +1030,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, */ /*! - \fn template T QJniObject::callStaticMethod(jclass clazz, const char *methodName) + \fn template Ret QJniObject::callStaticMethod(jclass clazz, const char *methodName, Args &&...args) + \since 6.4 Calls the static method \a methodName on \a clazz and returns the value. @@ -973,6 +1040,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, jclass javaMathClass = env.findClass("java/lang/Math"); jdouble randNr = QJniObject::callStaticMethod(javaMathClass, "random"); \endcode + + The method signature is deduced at compile time from \c Ret and the types of \a args. */ /*! @@ -1090,32 +1159,40 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, } /*! - \fn QJniObject QJniObject::callObjectMethod(const char *methodName) const + \fn template QJniObject QJniObject::callObjectMethod(const char *methodName, Args &&...args) const + \since 6.4 - Calls the Java objects method \a methodName and returns a new QJniObject for - the returned Java object. + Calls the Java objects method \a methodName with arguments \a args and returns a + new QJniObject for the returned Java object. \code QJniObject myJavaString = QJniObject::fromString("Hello, Java"); QJniObject myJavaString2 = myJavaString1.callObjectMethod("toString"); \endcode + + The method signature is deduced at compile time from \c Ret and the types of \a args. */ /*! - \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName) + \fn template QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, Args &&...args) + \since 6.4 - Calls the static method with \a methodName on the class \a className. + Calls the static method with \a methodName on the class \a className, passing + arguments \a args, and returns a new QJniObject for the returned Java object. \code QJniObject string = QJniObject::callStaticObjectMethod("CustomClass", "getClassName"); \endcode + + The method signature is deduced at compile time from \c Ret and the types of \a args. */ /*! - \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName) - - Calls the static method with \a methodName on \a clazz. + \fn template QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args) + \since 6.4 + Calls the static method with \a methodName on \a clazz, passing arguments \a args, + and returns a new QJniObject for the returned Java object. */ /*! diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index 829823efec..56700ddcbf 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -57,8 +57,26 @@ public: QJniObject(); explicit QJniObject(const char *className); explicit QJniObject(const char *className, const char *signature, ...); + template>...>>* = nullptr +#endif + > + explicit QJniObject(const char *className, Args &&...args) + : QJniObject(className, QtJniTypes::constructorSignature().data(), + std::forward(args)...) + {} explicit QJniObject(jclass clazz); explicit QJniObject(jclass clazz, const char *signature, ...); + template>...>>* = nullptr +#endif + > + explicit QJniObject(jclass clazz, Args &&...args) + : QJniObject(clazz, QtJniTypes::constructorSignature().data(), + std::forward(args)...) + {} QJniObject(jobject globalRef); ~QJniObject(); @@ -72,206 +90,135 @@ public: jclass objectClass() const; QByteArray className() const; - template - T callMethod(const char *methodName, const char *signature, ...) const - { - QtJniTypes::assertPrimitiveType(); - QJniEnvironment env; - T res{}; - jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); - if (id) { - va_list args; - va_start(args, signature); - callMethodForType(env.jniEnv(), res, object(), id, args); - va_end(args); - if (env.checkAndClearExceptions()) - res = {}; - } - return res; - } - - template <> - void callMethod(const char *methodName, const char *signature, ...) const + template + Ret callMethod(const char *methodName, const char *signature, Args &&...args) const { + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature); if (id) { - va_list args; - va_start(args, signature); - callVoidMethodV(env.jniEnv(), id, args); - va_end(args); - env.checkAndClearExceptions(); + if constexpr (std::is_same::value) { + callVoidMethodV(env.jniEnv(), id, std::forward(args)...); + env.checkAndClearExceptions(); + } else { + Ret res{}; + callMethodForType(env.jniEnv(), res, object(), id, std::forward(args)...); + if (env.checkAndClearExceptions()) + res = {}; + return res; + } + } + if constexpr (!std::is_same::value) + return Ret{}; + } + + template + Ret callMethod(const char *methodName, Args &&...args) const + { + constexpr auto signature = QtJniTypes::methodSignature(); + if constexpr (std::is_same::value) { + callMethod(methodName, signature.data(), std::forward(args)...); + } else { + QtJniTypes::assertPrimitiveType(); + return callMethod(methodName, signature.data(), std::forward(args)...); } } - template - T callMethod(const char *methodName) const + template + QJniObject callObjectMethod(const char *methodName, Args &&...args) const { - QtJniTypes::assertPrimitiveType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callMethod(methodName, signature); - } - - template - QJniObject callObjectMethod(const char *methodName) const - { - QtJniTypes::assertObjectType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callObjectMethod(methodName, signature); + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callObjectMethod(methodName, signature.data(), std::forward(args)...); } QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const; - template - static T callStaticMethod(const char *className, const char *methodName, - const char *signature, ...) - { - QtJniTypes::assertPrimitiveType(); - QJniEnvironment env; - T res{}; - jclass clazz = QJniObject::loadClass(className, env.jniEnv()); - if (clazz) { - jmethodID id = getCachedMethodID(env.jniEnv(), clazz, - QJniObject::toBinaryEncClassName(className), - methodName, signature, true); - if (id) { - va_list args; - va_start(args, signature); - callStaticMethodForType(env.jniEnv(), res, clazz, id, args); - va_end(args); - if (env.checkAndClearExceptions()) - res = {}; - } - } - return res; - } - - template <> - void callStaticMethod(const char *className, const char *methodName, - const char *signature, ...) + template + static Ret callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args) { + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; jclass clazz = QJniObject::loadClass(className, env.jniEnv()); - if (clazz) { - jmethodID id = getCachedMethodID(env.jniEnv(), clazz, - QJniObject::toBinaryEncClassName(className), - methodName, signature, true); - if (id) { - va_list args; - va_start(args, signature); - env->CallStaticVoidMethodV(clazz, id, args); - va_end(args); - env.checkAndClearExceptions(); - } - } + return callStaticMethod(clazz, methodName, signature, std::forward(args)...); } - template - static T callStaticMethod(const char *className, const char *methodName) - { - QtJniTypes::assertPrimitiveType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticMethod(className, methodName, signature); - } - - template - static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...) - { - QtJniTypes::assertPrimitiveType(); - QJniEnvironment env; - T res{}; - if (clazz) { - jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true); - if (id) { - va_list args; - va_start(args, signature); - callStaticMethodForType(env.jniEnv(), res, clazz, id, args); - va_end(args); - if (env.checkAndClearExceptions()) - res = {}; - } - } - return res; - } - - template <> - void callStaticMethod(jclass clazz, const char *methodName, - const char *signature, ...) + template + static Ret callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args) { + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; if (clazz) { jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true); - if (id) { - va_list args; - va_start(args, signature); - env->CallStaticVoidMethodV(clazz, id, args); - va_end(args); + return callStaticMethod(clazz, id, std::forward(args)...); + } + if constexpr (!std::is_same::value) + return Ret{}; + } + + template + static Ret callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args) + { + QtJniTypes::assertPrimitiveType(); + QJniEnvironment env; + if (clazz && methodId) { + if constexpr (std::is_same::value) { + callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward(args)...); env.checkAndClearExceptions(); + } else { + Ret res{}; + callStaticMethodForType(env.jniEnv(), res, clazz, methodId, std::forward(args)...); + if (env.checkAndClearExceptions()) + res = {}; + return res; } } + if constexpr (!std::is_same::value) + return Ret{}; } - template - static T callStaticMethod(jclass clazz, jmethodID methodId, ...) + template + static Ret callStaticMethod(const char *className, const char *methodName, Args &&...args) { - QtJniTypes::assertPrimitiveType(); + QtJniTypes::assertPrimitiveType(); QJniEnvironment env; - T res{}; - if (clazz && methodId) { - va_list args; - va_start(args, methodId); - callStaticMethodForType(env.jniEnv(), res, clazz, methodId, args); - va_end(args); - if (env.checkAndClearExceptions()) - res = {}; - } - return res; + jclass clazz = QJniObject::loadClass(className, env.jniEnv()); + return callStaticMethod(clazz, methodName, std::forward(args)...); } - template <> - void callStaticMethod(jclass clazz, jmethodID methodId, ...) + template + static Ret callStaticMethod(jclass clazz, const char *methodName, Args &&...args) { - QJniEnvironment env; - if (clazz && methodId) { - va_list args; - va_start(args, methodId); - env->CallStaticVoidMethodV(clazz, methodId, args); - va_end(args); - env.checkAndClearExceptions(); - } - } - - template static T callStaticMethod(jclass clazz, const char *methodName) - { - QtJniTypes::assertPrimitiveType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticMethod(clazz, methodName, signature); - } - - template - static QJniObject callStaticObjectMethod(const char *className, const char *methodName) - { - QtJniTypes::assertObjectType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticObjectMethod(className, methodName, signature); + QtJniTypes::assertPrimitiveType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticMethod(clazz, methodName, signature.data(), std::forward(args)...); } static QJniObject callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...); - template - static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName) - { - QtJniTypes::assertObjectType(); - constexpr auto signature = QtJniTypes::methodSignature(); - return callStaticObjectMethod(clazz, methodName, signature); - } - static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...); static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...); + + template + static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args) + { + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticObjectMethod(className, methodName, signature.data(), std::forward(args)...); + } + + template + static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args) + { + QtJniTypes::assertObjectType(); + constexpr auto signature = QtJniTypes::methodSignature(); + return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward(args)...); + } + template T getField(const char *fieldName) const { QtJniTypes::assertPrimitiveType(); @@ -490,7 +437,7 @@ private: static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name, const char *signature, bool isStatic = false); - void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const; + void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const; QJniObject callObjectMethodV(const char *methodName, const char *signature, va_list args) const; @@ -510,8 +457,11 @@ private: template static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, - jmethodID id, va_list args) + jmethodID id, ...) { + va_list args = {}; + va_start(args, id); + if constexpr(std::is_same::value) res = env->CallBooleanMethodV(obj, id, args); else if constexpr(std::is_same::value) @@ -530,12 +480,15 @@ private: res = env->CallDoubleMethodV(obj, id, args); else QtJniTypes::staticAssertTypeMismatch(); + va_end(args); } template static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, - jmethodID id, va_list args) + jmethodID id, ...) { + va_list args = {}; + va_start(args, id); if constexpr(std::is_same::value) res = env->CallStaticBooleanMethodV(clazz, id, args); else if constexpr(std::is_same::value) @@ -554,8 +507,18 @@ private: res = env->CallStaticDoubleMethodV(clazz, id, args); else QtJniTypes::staticAssertTypeMismatch(); + va_end(args); } + static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...) + { + va_list args; + va_start(args, id); + env->CallStaticVoidMethodV(clazz, id, args); + va_end(args); + } + + template static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) diff --git a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp index 8cf207fc3a..b3f420fd45 100644 --- a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp +++ b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp @@ -202,6 +202,10 @@ void tst_QJniEnvironment::findMethod() jmethodID methodId = env.findMethod(clazz, "toString", "()Ljava/lang/String;"); QVERIFY(methodId != nullptr); + // existing method + methodId = env.findMethod(clazz, "toString"); + QVERIFY(methodId != nullptr); + // invalid signature jmethodID invalid = env.findMethod(clazz, "unknown", "()I"); QVERIFY(invalid == nullptr); @@ -219,6 +223,10 @@ void tst_QJniEnvironment::findStaticMethod() jmethodID staticMethodId = env.findStaticMethod(clazz, "parseInt", "(Ljava/lang/String;)I"); QVERIFY(staticMethodId != nullptr); + // existing method + staticMethodId = env.findStaticMethod(clazz, "parseInt"); + QVERIFY(staticMethodId != nullptr); + QJniObject parameter = QJniObject::fromString("123"); jint result = QJniObject::callStaticMethod(clazz, staticMethodId, parameter.object()); @@ -227,6 +235,8 @@ void tst_QJniEnvironment::findStaticMethod() // invalid method jmethodID invalid = env.findStaticMethod(clazz, "unknown", "()I"); QVERIFY(invalid == nullptr); + invalid = env.findStaticMethod(clazz, "unknown"); + QVERIFY(invalid == nullptr); // check that all exceptions are already cleared QVERIFY(!env.checkAndClearExceptions()); } @@ -240,6 +250,8 @@ void tst_QJniEnvironment::findField() // valid field jfieldID validId = env.findField(clazz, "INT_FIELD", "I"); QVERIFY(validId != nullptr); + validId = env.findField(clazz, "INT_FIELD"); + QVERIFY(validId != nullptr); jmethodID constructorId = env.findMethod(clazz, "", "()V"); QVERIFY(constructorId != nullptr); @@ -265,6 +277,8 @@ void tst_QJniEnvironment::findStaticField() // valid field jfieldID validId = env.findStaticField(clazz, "S_INT_FIELD", "I"); QVERIFY(validId != nullptr); + validId = env.findStaticField(clazz, "S_INT_FIELD"); + QVERIFY(validId != nullptr); int size = env->GetStaticIntField(clazz, validId); QVERIFY(!env.checkAndClearExceptions()); diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 4c0d9e1e6e..669f31650e 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -166,6 +166,13 @@ void tst_QJniObject::ctor() QCOMPARE(string.toString(), object.toString()); } + { + QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java")); + QJniObject object("java/lang/String", string.object()); + QVERIFY(object.isValid()); + QCOMPARE(string.toString(), object.toString()); + } + { QJniEnvironment env; jclass javaStringClass = env->FindClass("java/lang/String"); @@ -182,6 +189,16 @@ void tst_QJniObject::ctor() QVERIFY(stringCpy.isValid()); QCOMPARE(qString, stringCpy.toString()); } + + { + QJniEnvironment env; + const QString qString = QLatin1String("Hello, Java"); + jclass javaStringClass = env->FindClass("java/lang/String"); + QJniObject string = QJniObject::fromString(qString); + QJniObject stringCpy(javaStringClass, string.object()); + QVERIFY(stringCpy.isValid()); + QCOMPARE(qString, stringCpy.toString()); + } } void tst_QJniObject::callMethodTest() @@ -194,9 +211,12 @@ void tst_QJniObject::callMethodTest() const jboolean isEmpty = jString1.callMethod("isEmpty"); QVERIFY(!isEmpty); - const jint ret = jString1.callMethod("compareToIgnoreCase", - "(Ljava/lang/String;)I", - jString2.object()); + jint ret = jString1.callMethod("compareToIgnoreCase", + "(Ljava/lang/String;)I", + jString2.object()); + QVERIFY(0 == ret); + + ret = jString1.callMethod("compareToIgnoreCase", jString2.object()); QVERIFY(0 == ret); } @@ -219,6 +239,10 @@ void tst_QJniObject::callObjectMethodTest() "(II)Ljava/lang/String;", 0, 4); QCOMPARE(subString.toString(), qString.mid(0, 4)); + + subString = jString.callObjectMethod("substring", 0, 4); + QCOMPARE(subString.toString(), qString.mid(0, 4)); + } void tst_QJniObject::stringConvertionTest() @@ -276,6 +300,16 @@ void tst_QJniObject::callStaticObjectMethodClassName() QString returnedString = returnValue.toString(); QCOMPARE(returnedString, QString::fromLatin1("test format")); + + returnValue = QJniObject::callStaticObjectMethod("java/lang/String", + "format", + formatString.object(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); } void tst_QJniObject::callStaticObjectMethod() @@ -297,6 +331,16 @@ void tst_QJniObject::callStaticObjectMethod() QString returnedString = returnValue.toString(); QCOMPARE(returnedString, QString::fromLatin1("test format")); + + returnValue = QJniObject::callStaticObjectMethod(cls, + "format", + formatString.object(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); } void tst_QJniObject::callStaticObjectMethodById() @@ -336,6 +380,9 @@ void tst_QJniObject::callStaticBooleanMethod() "(Ljava/lang/String;)Z", parameter.object()); QVERIFY(b); + + b = QJniObject::callStaticMethod(cls, "parseBoolean", parameter.object()); + QVERIFY(b); } { @@ -347,6 +394,9 @@ void tst_QJniObject::callStaticBooleanMethod() "(Ljava/lang/String;)Z", parameter.object()); QVERIFY(!b); + + b = QJniObject::callStaticMethod(cls, "parseBoolean", parameter.object()); + QVERIFY(!b); } } @@ -387,6 +437,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName() "(Ljava/lang/String;)Z", parameter.object()); QVERIFY(b); + b = QJniObject::callStaticMethod("java/lang/Boolean", + "parseBoolean", + parameter.object()); + QVERIFY(b); } { @@ -398,6 +452,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName() "(Ljava/lang/String;)Z", parameter.object()); QVERIFY(!b); + b = QJniObject::callStaticMethod("java/lang/Boolean", + "parseBoolean", + parameter.object()); + QVERIFY(!b); } } @@ -408,7 +466,6 @@ void tst_QJniObject::callStaticByteMethodClassName() jbyte returnValue = QJniObject::callStaticMethod("java/lang/Byte", "parseByte", - "(Ljava/lang/String;)B", parameter.object()); QCOMPARE(returnValue, jbyte(number.toInt())); } @@ -424,7 +481,6 @@ void tst_QJniObject::callStaticByteMethod() jbyte returnValue = QJniObject::callStaticMethod(cls, "parseByte", - "(Ljava/lang/String;)B", parameter.object()); QCOMPARE(returnValue, jbyte(number.toInt())); } @@ -452,7 +508,6 @@ void tst_QJniObject::callStaticIntMethodClassName() jint returnValue = QJniObject::callStaticMethod("java/lang/Integer", "parseInt", - "(Ljava/lang/String;)I", parameter.object()); QCOMPARE(returnValue, number.toInt()); } @@ -469,7 +524,6 @@ void tst_QJniObject::callStaticIntMethod() jint returnValue = QJniObject::callStaticMethod(cls, "parseInt", - "(Ljava/lang/String;)I", parameter.object()); QCOMPARE(returnValue, number.toInt()); } @@ -494,7 +548,6 @@ void tst_QJniObject::callStaticCharMethodClassName() { jchar returnValue = QJniObject::callStaticMethod("java/lang/Character", "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } @@ -508,7 +561,6 @@ void tst_QJniObject::callStaticCharMethod() jchar returnValue = QJniObject::callStaticMethod(cls, "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } @@ -533,7 +585,6 @@ void tst_QJniObject::callStaticDoubleMethodClassName () jdouble returnValue = QJniObject::callStaticMethod("java/lang/Double", "parseDouble", - "(Ljava/lang/String;)D", parameter.object()); QCOMPARE(returnValue, number.toDouble()); } @@ -550,7 +601,6 @@ void tst_QJniObject::callStaticDoubleMethod() jdouble returnValue = QJniObject::callStaticMethod(cls, "parseDouble", - "(Ljava/lang/String;)D", parameter.object()); QCOMPARE(returnValue, number.toDouble()); } @@ -579,7 +629,6 @@ void tst_QJniObject::callStaticFloatMethodClassName() jfloat returnValue = QJniObject::callStaticMethod("java/lang/Float", "parseFloat", - "(Ljava/lang/String;)F", parameter.object()); QCOMPARE(returnValue, number.toFloat()); } @@ -596,7 +645,6 @@ void tst_QJniObject::callStaticFloatMethod() jfloat returnValue = QJniObject::callStaticMethod(cls, "parseFloat", - "(Ljava/lang/String;)F", parameter.object()); QCOMPARE(returnValue, number.toFloat()); } @@ -624,7 +672,6 @@ void tst_QJniObject::callStaticShortMethodClassName() jshort returnValue = QJniObject::callStaticMethod("java/lang/Short", "parseShort", - "(Ljava/lang/String;)S", parameter.object()); QCOMPARE(returnValue, number.toShort()); } @@ -641,7 +688,6 @@ void tst_QJniObject::callStaticShortMethod() jshort returnValue = QJniObject::callStaticMethod(cls, "parseShort", - "(Ljava/lang/String;)S", parameter.object()); QCOMPARE(returnValue, number.toShort()); } @@ -669,7 +715,6 @@ void tst_QJniObject::callStaticLongMethodClassName() jlong returnValue = QJniObject::callStaticMethod("java/lang/Long", "parseLong", - "(Ljava/lang/String;)J", parameter.object()); QCOMPARE(returnValue, jlong(number.toLong())); } @@ -685,7 +730,6 @@ void tst_QJniObject::callStaticLongMethod() jlong returnValue = QJniObject::callStaticMethod(cls, "parseLong", - "(Ljava/lang/String;)J", parameter.object()); QCOMPARE(returnValue, jlong(number.toLong())); } @@ -1044,9 +1088,15 @@ void tst_QJniObject::templateApiCheck() 1, true, 'c'); + QJniObject::callStaticMethod(testClassName, + "staticVoidMethodWithArgs", + 1, + true, + 'c'); testClass.callMethod("voidMethod"); testClass.callMethod("voidMethodWithArgs", "(IZC)V", 1, true, 'c'); + testClass.callMethod("voidMethodWithArgs", 1, true, 'c'); // jboolean ----------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, "staticBooleanMethod")); @@ -1056,6 +1106,11 @@ void tst_QJniObject::templateApiCheck() true, true, true)); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticBooleanMethodWithArgs", + true, + true, + true)); QVERIFY(testClass.callMethod("booleanMethod")); QVERIFY(testClass.callMethod("booleanMethodWithArgs", @@ -1063,6 +1118,10 @@ void tst_QJniObject::templateApiCheck() true, true, true)); + QVERIFY(testClass.callMethod("booleanMethodWithArgs", + true, + true, + true)); // jbyte -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1073,9 +1132,15 @@ void tst_QJniObject::templateApiCheck() 1, 1, 1) == A_BYTE_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticByteMethodWithArgs", + jbyte(1), + jbyte(1), + jbyte(1)) == A_BYTE_VALUE); QVERIFY(testClass.callMethod("byteMethod") == A_BYTE_VALUE); QVERIFY(testClass.callMethod("byteMethodWithArgs", "(BBB)B", 1, 1, 1) == A_BYTE_VALUE); + QVERIFY(testClass.callMethod("byteMethodWithArgs", jbyte(1), jbyte(1), jbyte(1)) == A_BYTE_VALUE); // jchar -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1086,6 +1151,11 @@ void tst_QJniObject::templateApiCheck() jchar(1), jchar(1), jchar(1)) == A_CHAR_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticCharMethodWithArgs", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); QVERIFY(testClass.callMethod("charMethod") == A_CHAR_VALUE); QVERIFY(testClass.callMethod("charMethodWithArgs", @@ -1093,6 +1163,10 @@ void tst_QJniObject::templateApiCheck() jchar(1), jchar(1), jchar(1)) == A_CHAR_VALUE); + QVERIFY(testClass.callMethod("charMethodWithArgs", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); // jshort ------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1103,6 +1177,11 @@ void tst_QJniObject::templateApiCheck() jshort(1), jshort(1), jshort(1)) == A_SHORT_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticShortMethodWithArgs", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); QVERIFY(testClass.callMethod("shortMethod") == A_SHORT_VALUE); QVERIFY(testClass.callMethod("shortMethodWithArgs", @@ -1110,6 +1189,10 @@ void tst_QJniObject::templateApiCheck() jshort(1), jshort(1), jshort(1)) == A_SHORT_VALUE); + QVERIFY(testClass.callMethod("shortMethodWithArgs", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); // jint --------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1120,6 +1203,11 @@ void tst_QJniObject::templateApiCheck() jint(1), jint(1), jint(1)) == A_INT_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticIntMethodWithArgs", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); QVERIFY(testClass.callMethod("intMethod") == A_INT_VALUE); QVERIFY(testClass.callMethod("intMethodWithArgs", @@ -1127,6 +1215,10 @@ void tst_QJniObject::templateApiCheck() jint(1), jint(1), jint(1)) == A_INT_VALUE); + QVERIFY(testClass.callMethod("intMethodWithArgs", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); // jlong -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1137,6 +1229,11 @@ void tst_QJniObject::templateApiCheck() jlong(1), jlong(1), jlong(1)) == A_LONG_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticLongMethodWithArgs", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); QVERIFY(testClass.callMethod("longMethod") == A_LONG_VALUE); QVERIFY(testClass.callMethod("longMethodWithArgs", @@ -1144,6 +1241,10 @@ void tst_QJniObject::templateApiCheck() jlong(1), jlong(1), jlong(1)) == A_LONG_VALUE); + QVERIFY(testClass.callMethod("longMethodWithArgs", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); // jfloat ------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1154,6 +1255,11 @@ void tst_QJniObject::templateApiCheck() jfloat(1.1), jfloat(1.1), jfloat(1.1)) == A_FLOAT_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticFloatMethodWithArgs", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); QVERIFY(testClass.callMethod("floatMethod") == A_FLOAT_VALUE); QVERIFY(testClass.callMethod("floatMethodWithArgs", @@ -1161,6 +1267,10 @@ void tst_QJniObject::templateApiCheck() jfloat(1.1), jfloat(1.1), jfloat(1.1)) == A_FLOAT_VALUE); + QVERIFY(testClass.callMethod("floatMethodWithArgs", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); // jdouble ------------------------------------------------------------------------------------ QVERIFY(QJniObject::callStaticMethod(testClassName, @@ -1171,6 +1281,11 @@ void tst_QJniObject::templateApiCheck() jdouble(1.1), jdouble(1.1), jdouble(1.1)) == A_DOUBLE_VALUE); + QVERIFY(QJniObject::callStaticMethod(testClassName, + "staticDoubleMethodWithArgs", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); QVERIFY(testClass.callMethod("doubleMethod") == A_DOUBLE_VALUE); QVERIFY(testClass.callMethod("doubleMethodWithArgs", @@ -1178,6 +1293,10 @@ void tst_QJniObject::templateApiCheck() jdouble(1.1), jdouble(1.1), jdouble(1.1)) == A_DOUBLE_VALUE); + QVERIFY(testClass.callMethod("doubleMethodWithArgs", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); // jobject ------------------------------------------------------------------------------------ {