From 45f54ec61a54d05e516176a3f967755d6abc883d Mon Sep 17 00:00:00 2001 From: Jonah Graham Date: Wed, 24 Sep 2025 11:59:08 -0400 Subject: [PATCH] Improve support for Arrays of Strings in JNI Generator A number of places in the GTK4 API there is the possibility of passing in arrays of Strings, i.e. Java String[] -> char ** as input argument to C side. This change adds better support to the JNI generator for this use case by converting the jobjectArray (String[]) to char ** on the C side. Part of #2126 Split out of #2538 --- .../eclipse/swt/tools/internal/ASTType.java | 5 ++- .../swt/tools/internal/NativesGenerator.java | 24 ++++++++++ .../swt/tools/internal/ReflectType.java | 5 ++- .../META-INF/MANIFEST.MF | 2 +- .../Eclipse SWT PI/gtk/library/os_custom.c | 45 +++++++++++++++++++ .../Eclipse SWT PI/gtk/library/os_custom.h | 3 ++ .../org.eclipse.swt.tools.feature/feature.xml | 2 +- 7 files changed, 82 insertions(+), 4 deletions(-) diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTType.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTType.java index 1c9cfd55d4e..538e81ea1ed 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTType.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTType.java @@ -148,7 +148,10 @@ public String getTypeSignature2() { if (name.equals("Ljava/lang/String;")) return "jstring"; if (name.equals("Ljava/lang/Class;")) return "jclass"; if (isArray()) { - return getComponentType().getTypeSignature2() + "Array"; + if (getComponentType().isPrimitive()) { + return getComponentType().getTypeSignature2() + "Array"; + } + return "jobjectArray"; } return "jobject"; } diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/NativesGenerator.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/NativesGenerator.java index 0a5fb323b77..b89572f3a5b 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/NativesGenerator.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/NativesGenerator.java @@ -299,6 +299,14 @@ boolean generateGetParameter(JNIParameter param, boolean critical, int indent) { output(iStr); output(", NULL)"); } + } else if (componentType.isType("java.lang.String")) { + if (param.getFlag(FLAG_UNICODE)) { + throw new Error("not done"); + } + + output("swt_getArrayOfStringsUTF(env, arg"); + output(iStr); + output(")"); } else { throw new Error("not done"); } @@ -381,6 +389,16 @@ void generateSetParameter(JNIParameter param, boolean critical) { output("0"); } output(");"); + } else if (componentType.isType("java.lang.String")) { + if (param.getFlag(FLAG_UNICODE)) { + throw new Error("not done"); + } + + output("swt_releaseArrayOfStringsUTF(env, arg"); + output(iStr); + output(", lparg"); + output(iStr); + output(");"); } else { throw new Error("not done"); } @@ -452,6 +470,12 @@ boolean generateLocalVars(JNIParameter[] params, JNIType returnType) { output(componentType.getTypeSignature2()); output(" *lparg" + i); output("=NULL;"); + } else if (componentType.isType("java.lang.String")) { + if (param.getFlag(FLAG_UNICODE)) { + output("jchar **lparg" + i + "=NULL"); + } else { + output("char **lparg" + i+ "=NULL"); + } } else { throw new Error("not done"); } diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ReflectType.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ReflectType.java index 81d90b458cd..50fd124094f 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ReflectType.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ReflectType.java @@ -91,7 +91,10 @@ public String getTypeSignature2() { if (clazz == String.class) return "jstring"; if (clazz == Class.class) return "jclass"; if (clazz.isArray()) { - return getComponentType().getTypeSignature2() + "Array"; + if (getComponentType().isPrimitive()) { + return getComponentType().getTypeSignature2() + "Array"; + } + return "jobjectArray"; } return "jobject"; } diff --git a/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF b/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF index 8e889fffa57..7798f42e14a 100644 --- a/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.tools; singleton:=true -Bundle-Version: 3.110.800.qualifier +Bundle-Version: 3.110.900.qualifier Bundle-ManifestVersion: 2 Export-Package: org.eclipse.swt.tools.internal; x-internal:=true Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c index 94db05631a2..2b26e5a7ff7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c @@ -2187,3 +2187,48 @@ void swt_debug_on_fatal_warnings() { gtk_parse_args(&argcount, &arg2); } #endif + +/** + * Convert an array of Java strings to an array of char * + */ +char ** +swt_getArrayOfStringsUTF(JNIEnv *env, jobjectArray javaArray) { + jsize length = (*env)->GetArrayLength(env, javaArray); + + char **cStrings = (char **)malloc((length + 1) * sizeof(char*)); + if (!cStrings) { + return NULL; + } + + for (jsize i = 0; i < length; i++) { + jstring jstr = (jstring)(*env)->GetObjectArrayElement(env, javaArray, i); + if (jstr) { + const char *utfStr = (*env)->GetStringUTFChars(env, jstr, 0); + + cStrings[i] = strdup(utfStr); + + (*env)->ReleaseStringUTFChars(env, jstr, utfStr); + (*env)->DeleteLocalRef(env, jstr); + } else { + cStrings[i] = NULL; + } + } + + cStrings[length] = NULL; + return cStrings; +} + +/** + * Free the memory allocated by swt_getArrayOfStringsUTF + */ +void +swt_releaseArrayOfStringsUTF(JNIEnv *env, jobjectArray javaArray, char **cStrings) { + jsize length = (*env)->GetArrayLength(env, javaArray); + + for (jsize i = 0; i < length; i++) { + if (cStrings[i]) { + free(cStrings[i]); + } + } + free(cStrings); +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h index 03165c29900..e590b15a71e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h @@ -187,4 +187,7 @@ jlong call_accessible_object_function (const char *method_name, const char *meth void swt_set_lock_functions(); void swt_debug_on_fatal_warnings() ; +char **swt_getArrayOfStringsUTF(JNIEnv *env, jobjectArray javaArray); +void swt_releaseArrayOfStringsUTF(JNIEnv *env, jobjectArray javaArray, char **cStrings); + #endif /* ORG_ECLIPSE_SWT_GTK_OS_CUSTOM_H (include guard, this should be the last line) */ diff --git a/features/org.eclipse.swt.tools.feature/feature.xml b/features/org.eclipse.swt.tools.feature/feature.xml index 145eb4e15b4..06a466f73ae 100644 --- a/features/org.eclipse.swt.tools.feature/feature.xml +++ b/features/org.eclipse.swt.tools.feature/feature.xml @@ -2,7 +2,7 @@