diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f7a3de..5064167 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,15 @@ jobs: run: | make examples + - name: Check that the generated code and the examples build with -Wall -Werror + run: | + for f in examples/*/*.cpp; do + echo "Checking $f" + g++ -Wall -Werror -fsyntax-only \ + -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \ + -I"$(dirname "$f")" "$f" + done + package: runs-on: ubuntu-latest strategy: diff --git a/CHANGELOG b/CHANGELOG index 04d291c..d137a71 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -giws (3.1.0) +giws (4.0.0) * Generated code: the exception check after the Java call was only emitted for static methods. Non-static methods now detect a pending @@ -14,13 +14,26 @@ giws (3.1.0) * Generated GiwsException: check for pending exceptions after each CallObjectMethod (fixes -Xcheck:jni warnings while building the C++ exception) + * Generated GiwsException: release the JNI local references on the + early-return paths too (they leaked when embedding the JVM, as + local references are only freed when the thread detaches) + * Generated code: boolean arrays with --disable-return-size-array + wrote the array length through an uninitialized pointer + (undefined behavior); use a local variable like the other types + * Generated classes: make the destructor virtual (the class has + virtual methods; deleting through it was flagged by + -Wdelete-non-virtual-dtor) + * Examples: fix the -Wall warnings in the hand-written sources + (unused variables, VLA, catching a polymorphic type by value) + * CI: check that the generated code and the examples build with + -Wall -Werror * Add a test suite (tests/) run by CI; no JDK needed * New example bug_nonstatic_exception demonstrating the exception fix on non-static methods * Modernize the Python code (f-strings, type hints, with-statement) * Add a pre-commit configuration running ruff - -- Sylvestre Ledru Thu, 06 Aug 2026 22:23:44 +0200 + -- Sylvestre Ledru Fri, 07 Aug 2026 10:43:07 +0200 giws (2.0.3) diff --git a/CXXException.py b/CXXException.py index bb387d9..57a6208 100644 --- a/CXXException.py +++ b/CXXException.py @@ -403,7 +403,8 @@ def generateCXXBody(self, config): // retrieve information from the exception. // get method id - jmethodID getLocalizedMessageId = curEnv->GetMethodID(curEnv->GetObjectClass(javaException), + jclass javaExceptionClass = curEnv->GetObjectClass(javaException); + jmethodID getLocalizedMessageId = curEnv->GetMethodID(javaExceptionClass, "getLocalizedMessage", "()Ljava/lang/String;"); @@ -413,11 +414,13 @@ def generateCXXBody(self, config): if (curEnv->ExceptionCheck()) { curEnv->ExceptionClear(); + curEnv->DeleteLocalRef(javaExceptionClass); return ""; } if (description == NULL) { + curEnv->DeleteLocalRef(javaExceptionClass); return ""; } @@ -425,6 +428,7 @@ def generateCXXBody(self, config): // release java resources curEnv->DeleteLocalRef(description); + curEnv->DeleteLocalRef(javaExceptionClass); return res; } @@ -441,9 +445,11 @@ def generateCXXBody(self, config): // retrieve information from the exception. // get method id // getStackTrace returns an array of StackTraceElement - jmethodID getStackTraceId = curEnv->GetMethodID(curEnv->GetObjectClass(javaException), + jclass javaExceptionClass = curEnv->GetObjectClass(javaException); + jmethodID getStackTraceId = curEnv->GetMethodID(javaExceptionClass, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); + curEnv->DeleteLocalRef(javaExceptionClass); // call getStackTrace jobjectArray stackTrace = (jobjectArray) curEnv->CallObjectMethod(javaException, getStackTraceId); @@ -536,6 +542,8 @@ def generateCXXBody(self, config): if (javaName == NULL) { + curEnv->DeleteLocalRef(exceptionClass); + curEnv->DeleteLocalRef(classClass); return ""; } diff --git a/classRepresentation/objectGiws.py b/classRepresentation/objectGiws.py index 31bac8d..94742fe 100644 --- a/classRepresentation/objectGiws.py +++ b/classRepresentation/objectGiws.py @@ -428,7 +428,7 @@ def generateCXXHeader(self, packageName): %s // Destructor - ~%s(); + virtual ~%s(); // Generic method // Synchronization methods diff --git a/configGiws.py b/configGiws.py index 531e455..7a8a9cd 100644 --- a/configGiws.py +++ b/configGiws.py @@ -36,7 +36,7 @@ """Configuration of the Env""" -__version__ = "3.1.0" +__version__ = "4.0.0" class configGiws: diff --git a/datatypes/booleanDataGiws.py b/datatypes/booleanDataGiws.py index 0f2e986..ae3c97f 100644 --- a/datatypes/booleanDataGiws.py +++ b/datatypes/booleanDataGiws.py @@ -107,12 +107,17 @@ def specificPostProcessing(self, detachThread): if self.isArray(): str = JNIFrameWork().getExceptionCheckProfile(detachThread) strCommon = "" + strDeclaration = "" if configGiws().getDisableReturnSize() is True: - strCommon += "int *lenRow;" + strCommon += "int lenRow;" + else: + # The size of the array is returned as output argument of the + # function + strDeclaration = "*" strCommon += """ - *lenRow = curEnv->GetArrayLength(res); + %s lenRow = curEnv->GetArrayLength(res); jboolean isCopy = JNI_FALSE; - """ + """ % (strDeclaration) if self.getDimensionArray() == 1: return ( @@ -122,30 +127,31 @@ def specificPostProcessing(self, detachThread): /* faster than getXXXArrayElements */ jboolean *resultsArray = static_cast(curEnv->GetPrimitiveArrayCritical(res, &isCopy)); - bool * myArray= new bool[*lenRow]; + bool * myArray= new bool[%s lenRow]; - for (jsize i = 0; i < *lenRow; i++){ + for (jsize i = 0; i < %s lenRow; i++){ myArray[i]=(resultsArray[i] == JNI_TRUE); } curEnv->ReleasePrimitiveArrayCritical(res, resultsArray, JNI_ABORT); curEnv->DeleteLocalRef(res); """ + % (strDeclaration, strDeclaration) ) else: if configGiws().getDisableReturnSize() is True: - str += "int *lenCol;" + str += "int lenCol;" return ( str + strCommon + """ - bool ** myArray = new bool*[*lenRow]; - for(int i=0; i<*lenRow; i++) { + bool ** myArray = new bool*[%s lenRow]; + for(int i=0; i<%s lenRow; i++) { jbooleanArray oneDim = (jbooleanArray)curEnv->GetObjectArrayElement(res, i); - *lenCol=curEnv->GetArrayLength(oneDim); + %s lenCol=curEnv->GetArrayLength(oneDim); bool *resultsArray = static_cast(curEnv->GetPrimitiveArrayCritical(oneDim, &isCopy)); - myArray[i] = new bool[*lenCol]; - for(int j=0; j<*lenCol; j++) { + myArray[i] = new bool[%s lenCol]; + for(int j=0; j<%s lenCol; j++) { myArray[i][j]=(resultsArray[j] == JNI_TRUE); } curEnv->ReleasePrimitiveArrayCritical(res, resultsArray, JNI_ABORT); @@ -153,6 +159,13 @@ def specificPostProcessing(self, detachThread): curEnv->DeleteLocalRef(res); """ + % ( + strDeclaration, + strDeclaration, + strDeclaration, + strDeclaration, + strDeclaration, + ) ) else: diff --git a/examples/bug_disable_return/main.cpp b/examples/bug_disable_return/main.cpp index 9d97a92..a1fc784 100644 --- a/examples/bug_disable_return/main.cpp +++ b/examples/bug_disable_return/main.cpp @@ -2,7 +2,7 @@ #include "Plop.hxx" #include /* -Copyright or © or Copr. INRIA/Scilab - Sylvestre LEDRU +Copyright or � or Copr. INRIA/Scilab - Sylvestre LEDRU # Sylvestre LEDRU - @@ -64,5 +64,6 @@ int main(){ JavaVM* jvm = create_vm(); Plop *plop = new Plop(jvm); cout << "Does nothing. It is just to test the bug." << endl; + delete plop; return 0; } diff --git a/examples/bug_no_param_int_array/main.cpp b/examples/bug_no_param_int_array/main.cpp index a87eecb..c5608b7 100644 --- a/examples/bug_no_param_int_array/main.cpp +++ b/examples/bug_no_param_int_array/main.cpp @@ -2,7 +2,7 @@ #include "Bar.hxx" #include /* -Copyright or © or Copr. INRIA/Scilab - Sylvestre LEDRU +Copyright or � or Copr. INRIA/Scilab - Sylvestre LEDRU # Sylvestre LEDRU - @@ -64,5 +64,6 @@ int main(){ JavaVM* jvm = create_vm(); Bar *plop = new Bar(jvm); cout << "Does nothing" << endl; + delete plop; return 0; } diff --git a/examples/example2/main.cpp b/examples/example2/main.cpp index a1aa529..e5f0b40 100644 --- a/examples/example2/main.cpp +++ b/examples/example2/main.cpp @@ -1,5 +1,5 @@ /* -Copyright or © or Copr. INRIA/Scilab - Sylvestre LEDRU +Copyright or � or Copr. INRIA/Scilab - Sylvestre LEDRU # Sylvestre LEDRU - @@ -61,7 +61,7 @@ using namespace example2; using namespace std; int main(){ - int sizeArray=3; + const int sizeArray=3; JavaVM* jvm = create_vm(); MyObjectWithArray *plop = new MyObjectWithArray(jvm); int myArrayOfLong[sizeArray]; diff --git a/examples/example3/main.cpp b/examples/example3/main.cpp index bb789db..379dfec 100644 --- a/examples/example3/main.cpp +++ b/examples/example3/main.cpp @@ -1,5 +1,5 @@ /* -Copyright or © or Copr. INRIA/Scilab - Sylvestre LEDRU +Copyright or � or Copr. INRIA/Scilab - Sylvestre LEDRU # Sylvestre LEDRU - @@ -63,7 +63,6 @@ using namespace example3; using namespace std; int main(){ - int sizeArray=3; JavaVM* jvm = create_vm(); MyObjectWhichReturnsExceptions *plop = new MyObjectWhichReturnsExceptions(jvm); @@ -74,9 +73,9 @@ int main(){ cout << "Exception caught:" << endl; try { - int myIntsWithExceptionCatched = plop->getIntFromArrayOfSizeThree(22); + plop->getIntFromArrayOfSizeThree(22); - }catch(GiwsException::JniException e) { + }catch(const GiwsException::JniException & e) { cout << "getJavaDescription: " << e.getJavaDescription() << endl; cout << "getJavaStackTrace: " << e.getJavaStackTrace() << endl; cout << "getJavaExceptionName: " << e.getJavaExceptionName() << endl; @@ -86,8 +85,8 @@ int main(){ cout << "Exception will be trigger. This will display the message of the exception" << endl; try { - int value = plop->thisMethodWillFailWithMessage(); - }catch(GiwsException::JniException e) { + plop->thisMethodWillFailWithMessage(); + }catch(const GiwsException::JniException & e) { cout << "getJavaDescription: " << e.getJavaDescription() << endl; cout << "getJavaStackTrace: " << e.getJavaStackTrace() << endl; cout << "getJavaExceptionName: " << e.getJavaExceptionName() << endl; @@ -96,7 +95,7 @@ int main(){ cout << "========================" << endl; cout << "Exception not caught:" << endl; - int myIntsWithException = plop->getIntFromArrayOfSizeThree(223); + plop->getIntFromArrayOfSizeThree(223); cout << "Value from the Java with good pos : " << myInts <