1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3
4//go:build darwin && (arm64 || amd64)
5
6package purego_test
7
8import (
9 "os"
10 "path/filepath"
11 "testing"
12
13 "github.com/ebitengine/purego"
14)
15
16func TestRegisterFunc_structArgs(t *testing.T) {
17 libFileName := filepath.Join(t.TempDir(), "structtest.so")
18 t.Logf("Build %v", libFileName)
19
20 if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "struct_test.c")); err != nil {
21 t.Fatal(err)
22 }
23 defer os.Remove(libFileName)
24
25 lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
26 if err != nil {
27 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
28 }
29
30 const (
31 expectedUnsigned = 0xdeadbeef
32 expectedSigned = -123
33 expectedOdd = 12 + 23 + 46
34 expectedLong uint64 = 0xdeadbeefcafebabe
35 expectedFloat float32 = 10
36 expectedDouble float64 = 10
37 )
38
39 {
40 type Empty struct{}
41 var NoStruct func(Empty) int64
42 purego.RegisterLibFunc(&NoStruct, lib, "NoStruct")
43 if ret := NoStruct(Empty{}); ret != expectedUnsigned {
44 t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned)
45 }
46 }
47 {
48 type EmptyEmpty struct{}
49 var EmptyEmptyFn func(EmptyEmpty) int64
50 purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty")
51 if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned {
52 t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned)
53 }
54 var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64
55 purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg")
56 if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned {
57 t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned)
58 }
59 }
60 {
61 type GreaterThan16Bytes struct {
62 x, y, z *int64
63 }
64 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
65 var GreaterThan16BytesFn func(GreaterThan16Bytes) int64
66 purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes")
67 if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
68 t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned)
69 }
70 }
71 {
72 type GreaterThan16BytesStruct struct {
73 a struct {
74 x, y, z *int64
75 }
76 }
77 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
78 var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64
79 purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct")
80 if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned {
81 t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
82 }
83 }
84 {
85 type GreaterThan16Bytes struct {
86 x, y, z *int64
87 }
88 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
89 var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64
90 purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters")
91 if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
92 t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned)
93 }
94 var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64
95 z -= 0xFF
96 purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters")
97 if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned {
98 t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned)
99 }
100 }
101 {
102 type IntLessThan16Bytes struct {
103 x, y int64
104 }
105 var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64
106 purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes")
107 if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned {
108 t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
109 }
110 }
111 {
112 type FloatLessThan16Bytes struct {
113 x, y float32
114 }
115 var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32
116 purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes")
117 if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat {
118 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
119 }
120 }
121 {
122 type ThreeSmallFields struct {
123 x, y, z float32
124 }
125 var ThreeSmallFieldsFn func(ThreeSmallFields) float32
126 purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields")
127 if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
128 t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
129 }
130 }
131 {
132 type FloatAndInt struct {
133 x float32
134 y int32
135 }
136 var FloatAndIntFn func(FloatAndInt) float32
137 purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt")
138 if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat {
139 t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
140 }
141 }
142 {
143 type DoubleStruct struct {
144 x float64
145 }
146 var DoubleStructFn func(DoubleStruct) float64
147 purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct")
148 if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
149 t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble)
150 }
151 }
152 {
153 type TwoDoubleStruct struct {
154 x, y float64
155 }
156 var TwoDoubleStructFn func(TwoDoubleStruct) float64
157 purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct")
158 if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble {
159 t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble)
160 }
161 }
162 {
163 type TwoDoubleTwoStruct struct {
164 x struct {
165 x, y float64
166 }
167 }
168 var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64
169 purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct")
170 if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble {
171 t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble)
172 }
173 }
174 {
175 type ThreeDoubleStruct struct {
176 x, y, z float64
177 }
178 var ThreeDoubleStructFn func(ThreeDoubleStruct) float64
179 purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct")
180 if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble {
181 t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble)
182 }
183 }
184 {
185 type LargeFloatStruct struct {
186 a, b, c, d, e, f float64
187 }
188 var LargeFloatStructFn func(LargeFloatStruct) float64
189 purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct")
190 if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
191 t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
192 }
193 var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64
194 purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs")
195 if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
196 t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat)
197 }
198 }
199 {
200 type Rect struct {
201 x, y, w, h float64
202 }
203 var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64
204 purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs")
205 if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble {
206 t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble)
207 }
208 var RectangleSubtract func(rect Rect) float64
209 purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract")
210 if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble {
211 t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble)
212 }
213 var Rectangle func(rect Rect) float64
214 purego.RegisterLibFunc(&Rectangle, lib, "Rectangle")
215 if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
216 t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat)
217 }
218 }
219 {
220 type FloatArray struct {
221 a [2]float64
222 }
223 var FloatArrayFn func(rect FloatArray) float64
224 purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray")
225 if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble {
226 t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat)
227 }
228 }
229 {
230 type UnsignedChar4Bytes struct {
231 a, b, c, d byte
232 }
233 var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
234 purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes")
235 if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned {
236 t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
237 }
238 }
239 {
240 type UnsignedChar4BytesStruct struct {
241 x struct {
242 a byte
243 }
244 y struct {
245 b byte
246 }
247 z struct {
248 c byte
249 }
250 w struct {
251 d byte
252 }
253 }
254 var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32
255 purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct")
256 if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{
257 x: struct{ a byte }{a: 0xDE},
258 y: struct{ b byte }{b: 0xAD},
259 z: struct{ c byte }{c: 0xBE},
260 w: struct{ d byte }{d: 0xEF},
261 }); ret != expectedUnsigned {
262 t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
263 }
264 }
265 {
266 type Short struct {
267 a, b, c, d uint16
268 }
269 var ShortFn func(Short) uint64
270 purego.RegisterLibFunc(&ShortFn, lib, "Short")
271 if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
272 t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
273 }
274 }
275 {
276 type Int struct {
277 a, b uint32
278 }
279 var IntFn func(Int) uint64
280 purego.RegisterLibFunc(&IntFn, lib, "Int")
281 if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong {
282 t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong)
283 }
284 }
285 {
286 type Long struct {
287 a uint64
288 }
289 var LongFn func(Long) uint64
290 purego.RegisterLibFunc(&LongFn, lib, "Long")
291 if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong {
292 t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong)
293 }
294 }
295 {
296 type Char8Bytes struct {
297 a, b, c, d, e, f, g, h int8
298 }
299 var Char8BytesFn func(Char8Bytes) int32
300 purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes")
301 if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned {
302 t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned)
303 }
304 }
305 {
306 type Odd struct {
307 a, b, c byte
308 }
309 var OddFn func(Odd) int32
310 purego.RegisterLibFunc(&OddFn, lib, "Odd")
311 if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd {
312 t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd)
313 }
314 }
315 {
316 type Char2Short1 struct {
317 a, b byte
318 c uint16
319 }
320 var Char2Short1s func(Char2Short1) int32
321 purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s")
322 if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd {
323 t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd)
324 }
325 }
326 {
327 type SignedChar2Short1 struct {
328 a, b int8
329 c int16
330 }
331 var SignedChar2Short1Fn func(SignedChar2Short1) int32
332 purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1")
333 if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned {
334 t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned)
335 }
336 }
337 {
338 type Array4UnsignedChars struct {
339 a [4]uint8
340 }
341 var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32
342 purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars")
343 if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned {
344 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
345 }
346 }
347 {
348 type Array3UnsignedChar struct {
349 a [3]uint8
350 }
351 var Array3UnsignedChars func(chars Array3UnsignedChar) uint32
352 purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars")
353 if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned {
354 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
355 }
356 }
357 {
358 type Array2UnsignedShort struct {
359 a [2]uint16
360 }
361 var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32
362 purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts")
363 if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned {
364 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
365 }
366 }
367 {
368 type Array4Chars struct {
369 a [4]int8
370 }
371 var Array4CharsFn func(chars Array4Chars) int32
372 purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
373 if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned {
374 t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned)
375 }
376 }
377 {
378 type Array2Short struct {
379 a [2]int16
380 }
381 var Array2Shorts func(chars Array2Short) int32
382 purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts")
383 if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned {
384 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
385 }
386 }
387 {
388 type Array3Short struct {
389 a [3]int16
390 }
391 var Array3Shorts func(chars Array3Short) int32
392 purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts")
393 if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned {
394 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
395 }
396 }
397 {
398 type BoolStruct struct {
399 b bool
400 }
401 var BoolStructFn func(BoolStruct) bool
402 purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct")
403 if ret := BoolStructFn(BoolStruct{true}); ret != true {
404 t.Fatalf("BoolStructFn returned %v wanted %v", ret, true)
405 }
406 if ret := BoolStructFn(BoolStruct{false}); ret != false {
407 t.Fatalf("BoolStructFn returned %v wanted %v", ret, false)
408 }
409 }
410 {
411 type BoolFloat struct {
412 b bool
413 _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct
414 f float32
415 }
416 var BoolFloatFn func(BoolFloat) float32
417 purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat")
418 if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat {
419 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat)
420 }
421 if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat {
422 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat)
423 }
424 }
425 {
426 type point struct{ x, y float64 }
427 type size struct{ width, height float64 }
428 type Content struct {
429 point point
430 size size
431 }
432 var InitWithContentRect func(*int, Content, int32, int32, bool) uint64
433 purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect")
434 if ret := InitWithContentRect(new(int),
435 // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef
436 Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}},
437 15, 4, true); ret != expectedUnsigned {
438 t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned)
439 }
440 }
441}