From b6c6e369f5fa1d6769c92a1b06a74eb946945c2f Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Wed, 1 May 2024 13:54:43 -0600 Subject: [PATCH 1/8] Delay resolving super classes till referenced --- src/main/c/jpy_jtype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/c/jpy_jtype.c b/src/main/c/jpy_jtype.c index 6a8dad2e..fd709cec 100644 --- a/src/main/c/jpy_jtype.c +++ b/src/main/c/jpy_jtype.c @@ -171,7 +171,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) //printf("T2: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // ... before we can continue processing the super type ... - if (JType_InitSuperType(jenv, type, resolve) < 0) { + if (JType_InitSuperType(jenv, type, JNI_FALSE) < 0) { PyDict_DelItem(JPy_Types, typeKey); return NULL; } From 3568d46581afe9fceceeeab959a5e793c690f904 Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Thu, 2 May 2024 10:24:25 -0600 Subject: [PATCH 2/8] Fix a ref counting issue that led to crash in a PyType_Ready failure situation --- src/main/c/jpy_jobj.c | 12 +++++++----- src/main/c/jpy_jtype.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/c/jpy_jobj.c b/src/main/c/jpy_jobj.c index d8247dc1..bb4d3ad6 100644 --- a/src/main/c/jpy_jobj.c +++ b/src/main/c/jpy_jobj.c @@ -726,11 +726,13 @@ int JType_InitSlots(JPy_JType* type) typeObj = JTYPE_AS_PYTYPE(type); - #if defined(JPY_COMPAT_39P) - Py_SET_REFCNT(typeObj, 1); - #else - Py_REFCNT(typeObj) = 1; - #endif + // This is hacky to make the caller JType_GetType be able to call JPy_INCREF regardless if the type was already created, + // but it will cause a ref counting error when this function fails, and we need to remove the type from the registry + // #if defined(JPY_COMPAT_39P) + // Py_SET_REFCNT(typeObj, 1); + // #else + // Py_REFCNT(typeObj) = 1; + // #endif Py_SET_TYPE(typeObj, NULL); Py_SET_SIZE(typeObj, 0); // todo: The following lines are actually correct, but setting Py_TYPE(type) = &JType_Type results in an interpreter crash. Why? diff --git a/src/main/c/jpy_jtype.c b/src/main/c/jpy_jtype.c index fd709cec..69d60a05 100644 --- a/src/main/c/jpy_jtype.c +++ b/src/main/c/jpy_jtype.c @@ -179,7 +179,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) //printf("T3: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // ... and processing the component type. - if (JType_InitComponentType(jenv, type, resolve) < 0) { + if (JType_InitComponentType(jenv, type, JNI_FALSE) < 0) { PyDict_DelItem(JPy_Types, typeKey); return NULL; } @@ -194,6 +194,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) } JType_AddClassAttribute(jenv, type); + JPy_DECREF(typeKey); //printf("T5: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); @@ -226,8 +227,12 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) return NULL; } } - - JPy_INCREF(type); + + // Only increment the refcount when the type is found in the registry to guarantee that we return a new reference + if (typeValue != NULL) { + JPy_INCREF(type); + } + return type; } From eb3f41d8d86ea18e497e9c1e4d188c7c2c7ec0f3 Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Sun, 12 May 2024 14:29:25 -0600 Subject: [PATCH 3/8] Split the changes to keep the issue relevant ones --- src/main/c/jpy_jobj.c | 2 +- src/main/c/jpy_jtype.c | 14 ++++++++ .../jpy/fixtures/CyclicReferenceChild1.java | 30 +++++++++++++++++ .../jpy/fixtures/CyclicReferenceChild2.java | 17 ++++++++++ .../fixtures/CyclicReferenceGrandParent.java | 33 +++++++++++++++++++ .../jpy/fixtures/CyclicReferenceParent.java | 32 ++++++++++++++++++ src/test/python/jpy_gettype_test.py | 15 +++++++++ 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java create mode 100644 src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java create mode 100644 src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java create mode 100644 src/test/java/org/jpy/fixtures/CyclicReferenceParent.java diff --git a/src/main/c/jpy_jobj.c b/src/main/c/jpy_jobj.c index bb4d3ad6..d67ff684 100644 --- a/src/main/c/jpy_jobj.c +++ b/src/main/c/jpy_jobj.c @@ -726,7 +726,7 @@ int JType_InitSlots(JPy_JType* type) typeObj = JTYPE_AS_PYTYPE(type); - // This is hacky to make the caller JType_GetType be able to call JPy_INCREF regardless if the type was already created, + // This is hacky to make the caller JType_GetType able to call JPy_INCREF regardless if the type was already created, // but it will cause a ref counting error when this function fails, and we need to remove the type from the registry // #if defined(JPY_COMPAT_39P) // Py_SET_REFCNT(typeObj, 1); diff --git a/src/main/c/jpy_jtype.c b/src/main/c/jpy_jtype.c index 69d60a05..fc0c089f 100644 --- a/src/main/c/jpy_jtype.c +++ b/src/main/c/jpy_jtype.c @@ -171,8 +171,17 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) //printf("T2: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // ... before we can continue processing the super type ... + // Note, at this point, the Python type has been registered-but-yet-finalized (as done in JType_InitSlots). + // We need to delay resolving the super classes to when they are actually referenced, so that in a cyclic + // reference scenario, the super classes can still be finalized (reference-able by child classes), but NOT RESOLVED + // (its methods/fields remain absent in the type object). This is to avoid the case where its members reference some other + // registered-but-yet-finalized classes in the same class hierarchy, and these classes in turn have + // registered-but-yet-finalized super classes, causing JType_InitSlots to fail for one of them because its super + // class is not finalized. When this happens, the affected classes will not be properly resolved. if (JType_InitSuperType(jenv, type, JNI_FALSE) < 0) { PyDict_DelItem(JPy_Types, typeKey); + JPy_DECREF(typeKey); + JPy_DECREF(type); return NULL; } @@ -180,7 +189,10 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) // ... and processing the component type. if (JType_InitComponentType(jenv, type, JNI_FALSE) < 0) { + JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitComponentType() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); + JPy_DECREF(typeKey); + JPy_DECREF(type); return NULL; } @@ -190,6 +202,8 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) if (JType_InitSlots(type) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitSlots() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); + JPy_DECREF(typeKey); + JPy_DECREF(type); return NULL; } diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java b/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java new file mode 100644 index 00000000..4cc6712b --- /dev/null +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java @@ -0,0 +1,30 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public class CyclicReferenceChild1 extends CyclicReferenceParent { + private int x; + + private CyclicReferenceChild1(int x) { + this.x = x; + } + + public static CyclicReferenceChild1 of(int x) { + return new CyclicReferenceChild1(x); + } + + public int get_x() { + return this.x; + } +} diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java b/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java new file mode 100644 index 00000000..7a127056 --- /dev/null +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java @@ -0,0 +1,17 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public class CyclicReferenceChild2 extends CyclicReferenceParent { +} diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java b/src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java new file mode 100644 index 00000000..92c2fe62 --- /dev/null +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java @@ -0,0 +1,33 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public class CyclicReferenceGrandParent { + private int x; + public int z = 100; + + public CyclicReferenceGrandParent() { + } + + public void refChild2(CyclicReferenceChild2 child2) { + } + + public int grandParentMethod() { + return 888; + } + + public int get_x() { + return this.x; + } +} diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceParent.java b/src/test/java/org/jpy/fixtures/CyclicReferenceParent.java new file mode 100644 index 00000000..1b544ccb --- /dev/null +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceParent.java @@ -0,0 +1,32 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public abstract class CyclicReferenceParent extends CyclicReferenceGrandParent { + private int x; + public int y = 10; + + public static CyclicReferenceChild1 of(int x) { + return CyclicReferenceChild1.of(x); + } + + public int parentMethod() { + return 88; + } + + public int get_x() { + return this.x; + } + +} diff --git a/src/test/python/jpy_gettype_test.py b/src/test/python/jpy_gettype_test.py index f9b58b1c..a7233526 100644 --- a/src/test/python/jpy_gettype_test.py +++ b/src/test/python/jpy_gettype_test.py @@ -71,6 +71,21 @@ def test_issue_74(self): for i in range(200): jpy.get_type(java_type) + def test_cyclic_reference(self): + """ + Test if delaying resolving super classes doesn't break existing class reference pattern. + """ + j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") + j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") + j_child2 = j_child2_class() + + j_child1 = j_child1_class.of(8) + self.assertEqual(88, j_child1.parentMethod()) + self.assertEqual(888, j_child1.grandParentMethod()) + self.assertIsNone(j_child1.refChild2(j_child2)) + self.assertEqual(8, j_child1.get_x()) + self.assertEqual(10, j_child1.y) + self.assertEqual(100, j_child1.z) if __name__ == '__main__': From aa77dbdae6965a381c06ddc78aeefdd57866d628 Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Sun, 12 May 2024 18:46:12 -0600 Subject: [PATCH 4/8] Trivial code cleanup --- src/main/c/jpy_module.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/c/jpy_module.c b/src/main/c/jpy_module.c index f29cd040..3c4afe8a 100644 --- a/src/main/c/jpy_module.c +++ b/src/main/c/jpy_module.c @@ -800,11 +800,7 @@ PyObject* JType_CreateJavaByteBufferObj(JNIEnv* jenv, PyObject* pyObj) PyObject* JPy_byte_buffer_internal(JNIEnv* jenv, PyObject* self, PyObject* args) { - jobject byteBufferRef; PyObject* pyObj; - PyObject* newPyObj; - Py_buffer *pyBuffer; - JPy_JByteBufferObj* byteBuffer; if (!PyArg_ParseTuple(args, "O:byte_buffer", &pyObj)) { return NULL; From 50d9dce32128bfd3da7522d3155f75e4c7427f7b Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Mon, 13 May 2024 09:28:07 -0600 Subject: [PATCH 5/8] Minor code refactoring and more comments --- src/main/c/jpy_jobj.c | 3 ++- src/main/c/jpy_jtype.c | 14 +++++++------- src/test/python/jpy_gettype_test.py | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/c/jpy_jobj.c b/src/main/c/jpy_jobj.c index d67ff684..cdc571f1 100644 --- a/src/main/c/jpy_jobj.c +++ b/src/main/c/jpy_jobj.c @@ -727,7 +727,8 @@ int JType_InitSlots(JPy_JType* type) typeObj = JTYPE_AS_PYTYPE(type); // This is hacky to make the caller JType_GetType able to call JPy_INCREF regardless if the type was already created, - // but it will cause a ref counting error when this function fails, and we need to remove the type from the registry + // but it will cause a ref counting error when this function fails, and we need to remove the type from the registry. + // The proper ref counting needs to be done in JType_GetType(). // #if defined(JPY_COMPAT_39P) // Py_SET_REFCNT(typeObj, 1); // #else diff --git a/src/main/c/jpy_jtype.c b/src/main/c/jpy_jtype.c index fc0c089f..ab7997be 100644 --- a/src/main/c/jpy_jtype.c +++ b/src/main/c/jpy_jtype.c @@ -179,6 +179,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) // registered-but-yet-finalized super classes, causing JType_InitSlots to fail for one of them because its super // class is not finalized. When this happens, the affected classes will not be properly resolved. if (JType_InitSuperType(jenv, type, JNI_FALSE) < 0) { + JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitSuperType() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); JPy_DECREF(typeKey); JPy_DECREF(type); @@ -208,6 +209,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) } JType_AddClassAttribute(jenv, type); + // 'type' will be returned as a new reference. 'typeKey' needs to be released. JPy_DECREF(typeKey); //printf("T5: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); @@ -230,24 +232,22 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) return NULL; } - JPy_DECREF(typeKey); type = (JPy_JType*) typeValue; + // 'type' will be returned as a new reference. 'typeKey' needs to be released. + JPy_INCREF(type); + JPy_DECREF(typeKey); } JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: javaName=\"%s\", found=%d, resolve=%d, resolved=%d, type=%p\n", type->javaName, found, resolve, type->isResolved, type); if (!type->isResolved && resolve) { if (JType_ResolveType(jenv, type) < 0) { + JPy_DECREF(type); return NULL; } } - // Only increment the refcount when the type is found in the registry to guarantee that we return a new reference - if (typeValue != NULL) { - JPy_INCREF(type); - } - - return type; + return type; } /** diff --git a/src/test/python/jpy_gettype_test.py b/src/test/python/jpy_gettype_test.py index a7233526..9370cd37 100644 --- a/src/test/python/jpy_gettype_test.py +++ b/src/test/python/jpy_gettype_test.py @@ -73,7 +73,7 @@ def test_issue_74(self): def test_cyclic_reference(self): """ - Test if delaying resolving super classes doesn't break existing class reference pattern. + Test if delaying resolving super classes breaks existing class reference pattern. """ j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") From 995619eecd95d2e2cb7c0aaee9a33389519440b6 Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Tue, 21 May 2024 08:47:33 -0600 Subject: [PATCH 6/8] Add a test for array component tyep resolution --- src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java | 7 +++++++ src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java | 3 +++ src/test/python/jpy_gettype_test.py | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java b/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java index 4cc6712b..21fd6efb 100644 --- a/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java @@ -27,4 +27,11 @@ public static CyclicReferenceChild1 of(int x) { public int get_x() { return this.x; } + + public CyclicReferenceChild2[] getChild2s() { + CyclicReferenceChild2[] child2s = new CyclicReferenceChild2[2]; + child2s[0] = new CyclicReferenceChild2(); + child2s[1] = new CyclicReferenceChild2(); + return child2s; + } } diff --git a/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java b/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java index 7a127056..922e910d 100644 --- a/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java +++ b/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java @@ -14,4 +14,7 @@ */ @SuppressWarnings("UnusedDeclaration") public class CyclicReferenceChild2 extends CyclicReferenceParent { + public String getName() { + return "Child2: " + this.toString(); + } } diff --git a/src/test/python/jpy_gettype_test.py b/src/test/python/jpy_gettype_test.py index 9370cd37..6bbc3bb5 100644 --- a/src/test/python/jpy_gettype_test.py +++ b/src/test/python/jpy_gettype_test.py @@ -87,6 +87,14 @@ def test_cyclic_reference(self): self.assertEqual(10, j_child1.y) self.assertEqual(100, j_child1.z) + def test_component_type_resolution(self): + j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") + j_child1 = j_child1_class.of(8) + j_child2s = j_child1.getChild2s() + self.assertIn("[Lorg.jpy.fixtures.CyclicReferenceChild2;", repr(type(j_child2s))) + for j_child2 in j_child2s: + self.assertTrue(j_child2.getName().startswith("Child2")) + if __name__ == '__main__': print('\nRunning ' + __file__) From 4b47e0e24a54d3d702690cbaf6c232f6b3b4c16f Mon Sep 17 00:00:00 2001 From: jianfengmao Date: Tue, 21 May 2024 10:57:20 -0600 Subject: [PATCH 7/8] Remove unwanted comments and wrong refcounting --- src/main/c/jpy_jobj.c | 8 ------ src/main/c/jpy_jtype.c | 15 +++++------ .../org/jpy/fixtures/GetTypeFailureChild.java | 26 +++++++++++++++++++ .../jpy/fixtures/GetTypeFailureParent.java | 24 +++++++++++++++++ src/test/python/jpy_gettype_test.py | 5 ++++ 5 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 src/test/java/org/jpy/fixtures/GetTypeFailureChild.java create mode 100644 src/test/java/org/jpy/fixtures/GetTypeFailureParent.java diff --git a/src/main/c/jpy_jobj.c b/src/main/c/jpy_jobj.c index cdc571f1..84880a27 100644 --- a/src/main/c/jpy_jobj.c +++ b/src/main/c/jpy_jobj.c @@ -726,14 +726,6 @@ int JType_InitSlots(JPy_JType* type) typeObj = JTYPE_AS_PYTYPE(type); - // This is hacky to make the caller JType_GetType able to call JPy_INCREF regardless if the type was already created, - // but it will cause a ref counting error when this function fails, and we need to remove the type from the registry. - // The proper ref counting needs to be done in JType_GetType(). - // #if defined(JPY_COMPAT_39P) - // Py_SET_REFCNT(typeObj, 1); - // #else - // Py_REFCNT(typeObj) = 1; - // #endif Py_SET_TYPE(typeObj, NULL); Py_SET_SIZE(typeObj, 0); // todo: The following lines are actually correct, but setting Py_TYPE(type) = &JType_Type results in an interpreter crash. Why? diff --git a/src/main/c/jpy_jtype.c b/src/main/c/jpy_jtype.c index ab7997be..7798009d 100644 --- a/src/main/c/jpy_jtype.c +++ b/src/main/c/jpy_jtype.c @@ -156,7 +156,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) found = JNI_FALSE; - // Create a new type instance + // Create a new type instance, the refcount is set to 1 if this call succeeds type = JType_New(jenv, classRef, resolve); if (type == NULL) { JPy_DECREF(typeKey); @@ -166,6 +166,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) //printf("T1: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // In order to avoid infinite recursion, we have to register the new (but yet incomplete) type first... + // it will increment the refcount of type PyDict_SetItem(JPy_Types, typeKey, (PyObject*) type); //printf("T2: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); @@ -209,8 +210,9 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) } JType_AddClassAttribute(jenv, type); - // 'type' will be returned as a new reference. 'typeKey' needs to be released. + // 'type' will be refcount incremented and returned as a new reference later. 'typeKey' needs to be released. JPy_DECREF(typeKey); + JPy_DECREF(type); //printf("T5: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); @@ -233,8 +235,7 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) } type = (JPy_JType*) typeValue; - // 'type' will be returned as a new reference. 'typeKey' needs to be released. - JPy_INCREF(type); + // 'type' will be refcount incremented and returned as a new reference. 'typeKey' needs to be released. JPy_DECREF(typeKey); } @@ -242,12 +243,12 @@ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) if (!type->isResolved && resolve) { if (JType_ResolveType(jenv, type) < 0) { - JPy_DECREF(type); return NULL; } } - return type; + JPy_INCREF(type); + return type; } /** @@ -1093,7 +1094,6 @@ int JType_InitComponentType(JNIEnv* jenv, JPy_JType* type, jboolean resolve) if (type->componentType == NULL) { return -1; } - JPy_INCREF(type->componentType); } else { type->componentType = NULL; } @@ -1111,7 +1111,6 @@ int JType_InitSuperType(JNIEnv* jenv, JPy_JType* type, jboolean resolve) if (type->superType == NULL) { return -1; } - JPy_INCREF(type->superType); JPy_DELETE_LOCAL_REF(superClassRef); } else if (type->isInterface && JPy_JObject != NULL) { // This solves the problems that java.lang.Object methods can not be called on interfaces (https://github.com/bcdev/jpy/issues/57) diff --git a/src/test/java/org/jpy/fixtures/GetTypeFailureChild.java b/src/test/java/org/jpy/fixtures/GetTypeFailureChild.java new file mode 100644 index 00000000..b065b10f --- /dev/null +++ b/src/test/java/org/jpy/fixtures/GetTypeFailureChild.java @@ -0,0 +1,26 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public class GetTypeFailureChild extends GetTypeFailureParent { + private int x; + + private GetTypeFailureChild(int x) { + this.x = x; + } + + public static GetTypeFailureChild of(int x) { + return new GetTypeFailureChild(x); + } +} diff --git a/src/test/java/org/jpy/fixtures/GetTypeFailureParent.java b/src/test/java/org/jpy/fixtures/GetTypeFailureParent.java new file mode 100644 index 00000000..0e86a760 --- /dev/null +++ b/src/test/java/org/jpy/fixtures/GetTypeFailureParent.java @@ -0,0 +1,24 @@ +// +// Copyright 2024 jpy-consortium +// + +package org.jpy.fixtures; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * Used as a test class for the test cases in jpy_gettype_test.py + * + * @author Jianfeng Mao + */ +@SuppressWarnings("UnusedDeclaration") +public abstract class GetTypeFailureParent { + static { + toFail(); + } + + static void toFail() { + throw new RuntimeException("Can't be loaded!"); + } +} diff --git a/src/test/python/jpy_gettype_test.py b/src/test/python/jpy_gettype_test.py index 6bbc3bb5..cb319413 100644 --- a/src/test/python/jpy_gettype_test.py +++ b/src/test/python/jpy_gettype_test.py @@ -95,6 +95,11 @@ def test_component_type_resolution(self): for j_child2 in j_child2s: self.assertTrue(j_child2.getName().startswith("Child2")) + def test_fail_init_supertype(self): + with self.assertRaises(ValueError) as cm: + j_child_class = jpy.get_type("org.jpy.fixtures.GetTypeFailureChild") + print(str(cm.exception)) + if __name__ == '__main__': print('\nRunning ' + __file__) From cbf494a1a4d206e35412bd7cae3d1ab3a703358a Mon Sep 17 00:00:00 2001 From: Jianfeng Mao <4297243+jmao-denver@users.noreply.github.com> Date: Tue, 21 May 2024 14:54:05 -0600 Subject: [PATCH 8/8] Update src/test/python/jpy_gettype_test.py --- src/test/python/jpy_gettype_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/python/jpy_gettype_test.py b/src/test/python/jpy_gettype_test.py index cb319413..67e9504a 100644 --- a/src/test/python/jpy_gettype_test.py +++ b/src/test/python/jpy_gettype_test.py @@ -98,7 +98,6 @@ def test_component_type_resolution(self): def test_fail_init_supertype(self): with self.assertRaises(ValueError) as cm: j_child_class = jpy.get_type("org.jpy.fixtures.GetTypeFailureChild") - print(str(cm.exception)) if __name__ == '__main__':