Skip to content

Commit acc45b3

Browse files
committed
[GR-60735] Add ignored tests for ctypes return struct by value - unsupported feature for now.
PullRequest: graalpython/3624
2 parents 542f72d + 8d29d98 commit acc45b3

File tree

3 files changed

+121
-5
lines changed

3 files changed

+121
-5
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ctypes.py

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -37,8 +37,8 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939
import ctypes
40+
import os.path
4041
import struct
41-
import sys
4242

4343
from tests.cpyext import CPyExtTestCase, CPyExtType
4444

@@ -126,3 +126,102 @@ def test_buffer(self):
126126
assert buffer.format.startswith(b'>')
127127
assert struct.Struct(buffer.format).size == int_format.size
128128
assert buffer.shape == (2, 2)
129+
130+
# TODO: GR-60735, we cannot support this without NFI struct by value support
131+
def ignore_test_custom_libs():
132+
# 16B: returned in registers on System V AMD64 ABI
133+
class MySmallStruct1(ctypes.Structure):
134+
_fields_ = [("num", ctypes.c_int64), ("str", ctypes.c_char_p)]
135+
136+
# 16B incl. 3B padding: not returned in registers on System V AMD64 ABI because of multiple fields
137+
class MySmallStruct2(ctypes.Structure):
138+
_fields_ = [("num", ctypes.c_int32), ("str", ctypes.c_char_p), ("end", ctypes.c_int8)]
139+
140+
class MyLargeStruct(ctypes.Structure):
141+
_fields_ = [("str", ctypes.c_char_p),
142+
("num1", ctypes.c_int32),
143+
("num2", ctypes.c_int64),
144+
("num3", ctypes.c_double),
145+
("num4", ctypes.c_int16),
146+
("num5", ctypes.c_int8),
147+
("num6", ctypes.c_int32),
148+
("num7", ctypes.c_int32)]
149+
150+
from distutils.ccompiler import new_compiler
151+
import tempfile
152+
153+
cc = new_compiler()
154+
with tempfile.TemporaryDirectory() as tmp_dir:
155+
original_cwd = os.getcwd()
156+
try:
157+
os.chdir(tmp_dir)
158+
print(tmp_dir)
159+
with open('src.c', 'x') as f:
160+
f.write("""
161+
#include <stdint.h>
162+
163+
typedef struct {
164+
int32_t num;
165+
const char *data;
166+
} MySmallStruct1;
167+
168+
MySmallStruct1 get_small_struct1() {
169+
MySmallStruct1 s = {42, "hello world"};
170+
return s;
171+
}
172+
173+
typedef struct {
174+
int32_t num;
175+
const char *data;
176+
int8_t end;
177+
} MySmallStruct2;
178+
179+
MySmallStruct2 get_small_struct2() {
180+
MySmallStruct2 s = {42, "hello world", 42};
181+
return s;
182+
}
183+
184+
typedef struct {
185+
const char *data;
186+
int32_t num1;
187+
int64_t num2;
188+
double num3;
189+
int16_t num4;
190+
int8_t num5;
191+
int32_t num6;
192+
int32_t num7;
193+
} MyLargeStruct;
194+
195+
MyLargeStruct get_large_struct() {
196+
MyLargeStruct s = {"hello world", 42, 42, 42, 42, 42, 42, 42};
197+
return s;
198+
}
199+
""")
200+
cc.compile(['src.c'])
201+
cc.link_shared_lib(['src.o'], 'myshlib')
202+
203+
ctypes_lib = ctypes.CDLL(os.path.join(tmp_dir, 'libmyshlib.so'))
204+
205+
ctypes_lib.get_small_struct1.argtypes = []
206+
ctypes_lib.get_small_struct1.restype = MySmallStruct1
207+
result = ctypes_lib.get_small_struct1()
208+
assert result.num == 42, result.num
209+
210+
ctypes_lib.get_small_struct2.argtypes = []
211+
ctypes_lib.get_small_struct2.restype = MySmallStruct2
212+
result = ctypes_lib.get_small_struct2()
213+
assert result.num == 42, result.num
214+
assert result.end == 42, result.end
215+
216+
ctypes_lib.get_large_struct.argtypes = []
217+
ctypes_lib.get_large_struct.restype = MyLargeStruct
218+
result = ctypes_lib.get_large_struct()
219+
assert result.num1 == 42
220+
assert result.num2 == 42
221+
assert result.num3 == 42
222+
assert result.num4 == 42
223+
assert result.num5 == 42
224+
assert result.num6 == 42
225+
assert result.num7 == 42
226+
finally:
227+
os.chdir(original_cwd)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -44,6 +44,7 @@
4444
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PyCFuncPtr;
4545
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PyCPointer;
4646
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PyCPointerType;
47+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
4748
import static com.oracle.graal.python.builtins.modules.ctypes.CDataTypeBuiltins.PyCData_GetContainer;
4849
import static com.oracle.graal.python.builtins.modules.ctypes.CtypesNodes.WCHAR_T_ENCODING;
4950
import static com.oracle.graal.python.builtins.modules.ctypes.CtypesNodes.WCHAR_T_SIZE;
@@ -1401,7 +1402,22 @@ static Object callGetFunc(VirtualFrame frame, Object restype, FFIType rtype, Obj
14011402
case FFI_TYPE_FLOAT -> Pointer.create(rtype, rtype.size, ilib.asFloat(result), 0);
14021403
case FFI_TYPE_DOUBLE -> Pointer.create(rtype, rtype.size, ilib.asDouble(result), 0);
14031404
case FFI_TYPE_VOID -> Pointer.NULL;
1404-
case FFI_TYPE_POINTER, FFI_TYPE_STRUCT -> {
1405+
case FFI_TYPE_STRUCT -> {
1406+
// TODO: NFI support for structs
1407+
// In case of struct, depending on the ABI the value returned from the
1408+
// function may be actual struct memory (<16B and some other constraints on
1409+
// SystemV AMD64 ABI, returned in two registers) or a pointer to caller
1410+
// allocated buffer prepared for struct contents (larger structs), in which
1411+
// case, however, the caller must allocate and pass the buffer as (hidden)
1412+
// argument. We cannot get away with just using NFI pointer type as return
1413+
// value for large structs, because NFI still needs to allocate and pass the
1414+
// buffer for the result to the callee.
1415+
CompilerDirectives.transferToInterpreterAndInvalidate();
1416+
throw PRaiseNode.raiseUncached(inliningTarget, SystemError, ErrorMessages.RETURNING_STRUCT_BY_VALUE_NOT_SUPPORTED);
1417+
}
1418+
case FFI_TYPE_POINTER -> {
1419+
// NOTE: we are returning pointer to the result buffer and the result buffer
1420+
// data itself is a pointer
14051421
Pointer pointer;
14061422
if (ilib.isNull(result)) {
14071423
pointer = Pointer.NULL;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -1185,6 +1185,7 @@ public abstract class ErrorMessages {
11851185

11861186
// ctypes
11871187
public static final TruffleString PASSING_STRUCTS_BY_VALUE_NOT_SUPPORTED = tsLiteral("Passing structs by value is not supported on NFI backend");
1188+
public static final TruffleString RETURNING_STRUCT_BY_VALUE_NOT_SUPPORTED = tsLiteral("ctypes: returning struct by value is not supported.");
11881189
public static final TruffleString MEMORYVIEW_CANNOT_BE_CONVERTED_TO_NATIVE_MEMORY = tsLiteral("Memoryview cannot be converted to native memory");
11891190
public static final TruffleString CANNOT_CONVERT_OBJECT_POINTER_TO_NATIVE = tsLiteral("Cannot convert Object pointer to native");
11901191
public static final TruffleString CANNOT_APPLY_OFFSET_TO_AN_OBJECT_POINTER = tsLiteral("Cannot apply offset to an object pointer");

0 commit comments

Comments
 (0)