1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3
4//go:build (darwin || linux) && (amd64 || arm64)
5
6package purego_test
7
8import (
9 "math"
10 "os"
11 "path/filepath"
12 "reflect"
13 "runtime"
14 "testing"
15 "unsafe"
16
17 "github.com/ebitengine/purego"
18)
19
20func TestRegisterFunc_structArgs(t *testing.T) {
21 libFileName := filepath.Join(t.TempDir(), "structtest.so")
22 t.Logf("Build %v", libFileName)
23
24 if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "struct_test.c")); err != nil {
25 t.Fatal(err)
26 }
27 defer os.Remove(libFileName)
28
29 lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
30 if err != nil {
31 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
32 }
33
34 const (
35 expectedUnsigned = 0xdeadbeef
36 expectedSigned = -123
37 expectedOdd = 12 + 23 + 46
38 expectedLong uint64 = 0xdeadbeefcafebabe
39 expectedFloat float32 = 10
40 expectedDouble float64 = 10
41 )
42
43 implementations := []struct {
44 name string
45 register func(fptr any, handle uintptr, name string, goFn any)
46 }{
47 {
48 name: "RegisterLibFunc",
49 register: func(fptr any, handle uintptr, name string, _ any) {
50 purego.RegisterLibFunc(fptr, handle, name)
51 },
52 },
53 {
54 name: "GoCallbackFunc",
55 register: func(fptr any, handle uintptr, name string, goFn any) {
56 fnType := reflect.TypeOf(fptr).Elem()
57 if fnType.NumOut() > 0 {
58 kind := fnType.Out(0).Kind()
59 if kind == reflect.Float32 || kind == reflect.Float64 {
60 // NewCallback doesn't support float returns.
61 // Float struct args are covered by identity tests that return structs.
62 purego.RegisterLibFunc(fptr, handle, name)
63 return
64 }
65 }
66 purego.RegisterFunc(fptr, purego.NewCallback(goFn))
67 },
68 },
69 }
70 for _, imp := range implementations {
71 imp := imp
72 t.Run(imp.name, func(t *testing.T) {
73 register := imp.register
74 {
75 type Empty struct{}
76 var NoStruct func(Empty) int64
77 register(&NoStruct, lib, "NoStruct", func(Empty) int64 {
78 return 0xdeadbeef
79 })
80 if ret := NoStruct(Empty{}); ret != expectedUnsigned {
81 t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned)
82 }
83 }
84 {
85 type EmptyEmpty struct{}
86 var EmptyEmptyFn func(EmptyEmpty) int64
87 register(&EmptyEmptyFn, lib, "EmptyEmpty", func(EmptyEmpty) int64 {
88 return 0xdeadbeef
89 })
90 if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned {
91 t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned)
92 }
93 var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64
94 register(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg", func(x uint32, _ EmptyEmpty, y uint32) int64 {
95 return int64(x)<<16 | int64(y)
96 })
97 if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned {
98 t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned)
99 }
100 }
101 {
102 type GreaterThan16Bytes struct {
103 x, y, z *int64
104 }
105 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
106 var GreaterThan16BytesFn func(GreaterThan16Bytes) int64
107 register(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes", func(g GreaterThan16Bytes) int64 {
108 return *g.x + *g.y + *g.z
109 })
110 if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
111 t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned)
112 }
113 }
114 {
115 type GreaterThan16BytesStruct struct {
116 a struct {
117 x, y, z *int64
118 }
119 }
120 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
121 var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64
122 register(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct", func(g GreaterThan16BytesStruct) int64 {
123 return *g.a.x + *g.a.y + *g.a.z
124 })
125 if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned {
126 t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
127 }
128 }
129 {
130 type GreaterThan16Bytes struct {
131 x, y, z *int64
132 }
133 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
134 var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64
135 register(&AfterRegisters, lib, "AfterRegisters", func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 {
136 registers := int64(a + b + c + d + e + f + g + h)
137 stack := *bytes.x + *bytes.y + *bytes.z
138 if registers != stack {
139 return 0xbadbad
140 }
141 if stack != 0xdeadbeef {
142 return 0xcafebad
143 }
144 return stack
145 })
146 if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
147 t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned)
148 }
149 var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64
150 z -= 0xFF
151 register(&BeforeRegisters, lib, "BeforeRegisters", func(bytes GreaterThan16Bytes, a, b int64) uint64 {
152 return uint64(*bytes.x + *bytes.y + *bytes.z + a + b)
153 })
154 if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned {
155 t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned)
156 }
157 }
158 {
159 type IntLessThan16Bytes struct {
160 x, y int64
161 }
162 var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64
163 register(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes", func(l IntLessThan16Bytes) int64 {
164 return l.x + l.y
165 })
166 if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned {
167 t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
168 }
169 }
170 {
171 type FloatLessThan16Bytes struct {
172 x, y float32
173 }
174 var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32
175 register(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes", func(f FloatLessThan16Bytes) float32 {
176 return f.x + f.y
177 })
178 if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat {
179 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
180 }
181 }
182 {
183 type ThreeSmallFields struct {
184 x, y, z float32
185 }
186 var ThreeSmallFieldsFn func(ThreeSmallFields) float32
187 register(&ThreeSmallFieldsFn, lib, "ThreeSmallFields", func(f ThreeSmallFields) float32 {
188 return f.x + f.y + f.z
189 })
190 if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
191 t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
192 }
193 }
194 {
195 type FloatAndInt struct {
196 x float32
197 y int32
198 }
199 var FloatAndIntFn func(FloatAndInt) float32
200 register(&FloatAndIntFn, lib, "FloatAndInt", func(f FloatAndInt) float32 {
201 return f.x + float32(f.y)
202 })
203 if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat {
204 t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
205 }
206 }
207 {
208 type DoubleStruct struct {
209 x float64
210 }
211 var DoubleStructFn func(DoubleStruct) float64
212 register(&DoubleStructFn, lib, "DoubleStruct", func(d DoubleStruct) float64 {
213 return d.x
214 })
215 if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
216 t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble)
217 }
218 }
219 {
220 type TwoDoubleStruct struct {
221 x, y float64
222 }
223 var TwoDoubleStructFn func(TwoDoubleStruct) float64
224 register(&TwoDoubleStructFn, lib, "TwoDoubleStruct", func(d TwoDoubleStruct) float64 {
225 return d.x + d.y
226 })
227 if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble {
228 t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble)
229 }
230 }
231 {
232 type TwoDoubleTwoStruct struct {
233 x struct {
234 x, y float64
235 }
236 }
237 var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64
238 register(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct", func(d TwoDoubleTwoStruct) float64 {
239 return d.x.x + d.x.y
240 })
241 if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble {
242 t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble)
243 }
244 }
245 {
246 type ThreeDoubleStruct struct {
247 x, y, z float64
248 }
249 var ThreeDoubleStructFn func(ThreeDoubleStruct) float64
250 register(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct", func(d ThreeDoubleStruct) float64 {
251 return d.x + d.y + d.z
252 })
253 if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble {
254 t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble)
255 }
256 }
257 {
258 type LargeFloatStruct struct {
259 a, b, c, d, e, f float64
260 }
261 var LargeFloatStructFn func(LargeFloatStruct) float64
262 register(&LargeFloatStructFn, lib, "LargeFloatStruct", func(s LargeFloatStruct) float64 {
263 return s.a + s.b + s.c + s.d + s.e + s.f
264 })
265 if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
266 t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
267 }
268 var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64
269 register(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs", func(a, b, c float64, s LargeFloatStruct) float64 {
270 return a + b + c + s.a + s.b + s.c + s.d + s.e + s.f
271 })
272 if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
273 t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat)
274 }
275 }
276 {
277 type Rect struct {
278 x, y, w, h float64
279 }
280 var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64
281 register(&RectangleWithRegs, lib, "RectangleWithRegs", func(a, b, c, d, e float64, rect Rect) float64 {
282 return a + b + c + d + e + rect.x + rect.y + rect.w + rect.h
283 })
284 if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble {
285 t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble)
286 }
287 var RectangleSubtract func(rect Rect) float64
288 register(&RectangleSubtract, lib, "RectangleSubtract", func(rect Rect) float64 {
289 return (rect.x + rect.y) - (rect.w + rect.h)
290 })
291 if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble {
292 t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble)
293 }
294 var Rectangle func(rect Rect) float64
295 register(&Rectangle, lib, "Rectangle", func(rect Rect) float64 {
296 return rect.x + rect.y + rect.w + rect.h
297 })
298 if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
299 t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat)
300 }
301 }
302 {
303 type FloatArray struct {
304 a [2]float64
305 }
306 var FloatArrayFn func(rect FloatArray) float64
307 register(&FloatArrayFn, lib, "FloatArray", func(f FloatArray) float64 {
308 return f.a[0] + f.a[1]
309 })
310 if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble {
311 t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat)
312 }
313 }
314 {
315 type UnsignedChar4Bytes struct {
316 a, b, c, d byte
317 }
318 var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
319 register(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes", func(b UnsignedChar4Bytes) uint32 {
320 return uint32(b.a)<<24 | uint32(b.b)<<16 | uint32(b.c)<<8 | uint32(b.d)
321 })
322 if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned {
323 t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
324 }
325 }
326 {
327 type UnsignedChar4BytesStruct struct {
328 x struct {
329 a byte
330 }
331 y struct {
332 b byte
333 }
334 z struct {
335 c byte
336 }
337 w struct {
338 d byte
339 }
340 }
341 var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32
342 register(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct", func(b UnsignedChar4BytesStruct) uint32 {
343 return uint32(b.x.a)<<24 | uint32(b.y.b)<<16 | uint32(b.z.c)<<8 | uint32(b.w.d)
344 })
345 if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{
346 x: struct{ a byte }{a: 0xDE},
347 y: struct{ b byte }{b: 0xAD},
348 z: struct{ c byte }{c: 0xBE},
349 w: struct{ d byte }{d: 0xEF},
350 }); ret != expectedUnsigned {
351 t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
352 }
353 }
354 {
355 type Short struct {
356 a, b, c, d uint16
357 }
358 var ShortFn func(Short) uint64
359 register(&ShortFn, lib, "Short", func(s Short) uint64 {
360 return uint64(s.a)<<48 | uint64(s.b)<<32 | uint64(s.c)<<16 | uint64(s.d)
361 })
362 if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
363 t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
364 }
365 }
366 {
367 type Int struct {
368 a, b uint32
369 }
370 var IntFn func(Int) uint64
371 register(&IntFn, lib, "Int", func(i Int) uint64 {
372 return uint64(i.a)<<32 | uint64(i.b)
373 })
374 if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong {
375 t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong)
376 }
377 }
378 {
379 type Long struct {
380 a uint64
381 }
382 var LongFn func(Long) uint64
383 register(&LongFn, lib, "Long", func(l Long) uint64 {
384 return l.a
385 })
386 if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong {
387 t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong)
388 }
389 }
390 {
391 type Char8Bytes struct {
392 a, b, c, d, e, f, g, h int8
393 }
394 var Char8BytesFn func(Char8Bytes) int32
395 register(&Char8BytesFn, lib, "Char8Bytes", func(b Char8Bytes) int32 {
396 return int32(b.a) + int32(b.b) + int32(b.c) + int32(b.d) + int32(b.e) + int32(b.f) + int32(b.g) + int32(b.h)
397 })
398 if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned {
399 t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned)
400 }
401 }
402 {
403 type Odd struct {
404 a, b, c byte
405 }
406 var OddFn func(Odd) int32
407 register(&OddFn, lib, "Odd", func(o Odd) int32 {
408 return int32(o.a) + int32(o.b) + int32(o.c)
409 })
410 if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd {
411 t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd)
412 }
413 }
414 {
415 type Char2Short1 struct {
416 a, b byte
417 c uint16
418 }
419 var Char2Short1s func(Char2Short1) int32
420 register(&Char2Short1s, lib, "Char2Short1s", func(s Char2Short1) int32 {
421 return int32(s.a) + int32(s.b) + int32(s.c)
422 })
423 if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd {
424 t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd)
425 }
426 }
427 {
428 type SignedChar2Short1 struct {
429 a, b int8
430 c int16
431 }
432 var SignedChar2Short1Fn func(SignedChar2Short1) int32
433 register(&SignedChar2Short1Fn, lib, "SignedChar2Short1", func(s SignedChar2Short1) int32 {
434 return int32(s.a) + int32(s.b) + int32(s.c)
435 })
436 if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned {
437 t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned)
438 }
439 }
440 {
441 type Array4UnsignedChars struct {
442 a [4]uint8
443 }
444 var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32
445 register(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars", func(a Array4UnsignedChars) uint32 {
446 return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | uint32(a.a[3])
447 })
448 if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned {
449 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
450 }
451 }
452 {
453 type Array3UnsignedChar struct {
454 a [3]uint8
455 }
456 var Array3UnsignedChars func(chars Array3UnsignedChar) uint32
457 register(&Array3UnsignedChars, lib, "Array3UnsignedChars", func(a Array3UnsignedChar) uint32 {
458 return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | 0xef
459 })
460 if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned {
461 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
462 }
463 }
464 {
465 type Array2UnsignedShort struct {
466 a [2]uint16
467 }
468 var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32
469 register(&Array2UnsignedShorts, lib, "Array2UnsignedShorts", func(a Array2UnsignedShort) uint32 {
470 return uint32(a.a[0])<<16 | uint32(a.a[1])
471 })
472 if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned {
473 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
474 }
475 }
476 {
477 type Array4Chars struct {
478 a [4]int8
479 }
480 var Array4CharsFn func(chars Array4Chars) int32
481 register(&Array4CharsFn, lib, "Array4Chars", func(a Array4Chars) int32 {
482 return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2]) + int32(a.a[3])
483 })
484 const expectedSum = 10 + 20 + 30 + 63
485 if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum {
486 t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum)
487 }
488 }
489 {
490 type Array2Short struct {
491 a [2]int16
492 }
493 var Array2Shorts func(chars Array2Short) int32
494 register(&Array2Shorts, lib, "Array2Shorts", func(a Array2Short) int32 {
495 return int32(a.a[0]) + int32(a.a[1])
496 })
497 if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned {
498 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
499 }
500 }
501 {
502 type Array3Short struct {
503 a [3]int16
504 }
505 var Array3Shorts func(chars Array3Short) int32
506 register(&Array3Shorts, lib, "Array3Shorts", func(a Array3Short) int32 {
507 return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2])
508 })
509 if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned {
510 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
511 }
512 }
513 {
514 type BoolStruct struct {
515 b bool
516 }
517 var BoolStructFn func(BoolStruct) bool
518 register(&BoolStructFn, lib, "BoolStruct", func(b BoolStruct) bool {
519 return b.b
520 })
521 if ret := BoolStructFn(BoolStruct{true}); ret != true {
522 t.Fatalf("BoolStructFn returned %v wanted %v", ret, true)
523 }
524 if ret := BoolStructFn(BoolStruct{false}); ret != false {
525 t.Fatalf("BoolStructFn returned %v wanted %v", ret, false)
526 }
527 }
528 {
529 type BoolFloat struct {
530 b bool
531 _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct
532 f float32
533 }
534 var BoolFloatFn func(BoolFloat) float32
535 register(&BoolFloatFn, lib, "BoolFloat", func(s BoolFloat) float32 {
536 if s.b {
537 return s.f
538 }
539 return -s.f
540 })
541 if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat {
542 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat)
543 }
544 if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat {
545 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat)
546 }
547 }
548 {
549 type point struct{ x, y float64 }
550 type size struct{ width, height float64 }
551 type Content struct {
552 point point
553 size size
554 }
555 var InitWithContentRect func(*int, Content, int32, int32, bool) uint64
556 register(&InitWithContentRect, lib, "InitWithContentRect", func(win *int, c Content, style, backing int32, flag bool) uint64 {
557 if win == nil {
558 return 0xBAD
559 }
560 if !flag {
561 return 0xF1A6
562 }
563 return uint64(c.point.x+c.point.y+c.size.width+c.size.height) / uint64(style-backing)
564 })
565 if ret := InitWithContentRect(new(int),
566 // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef
567 Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}},
568 15, 4, true); ret != expectedUnsigned {
569 t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned)
570 }
571 }
572 {
573 type GoInt4 struct {
574 A, B, C, D int
575 }
576 var GoInt4Fn func(GoInt4) int
577 register(&GoInt4Fn, lib, "GoInt4", func(g GoInt4) int {
578 return g.A + g.B - g.C + g.D
579 })
580 const expected = math.MaxInt - 52 - 3 + 4
581 if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected {
582 t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected)
583 }
584 }
585 {
586 type GoUint4 struct {
587 A, B, C, D uint
588 }
589 var GoUint4Fn func(GoUint4) uint
590 register(&GoUint4Fn, lib, "GoUint4", func(g GoUint4) uint {
591 return g.A + g.B + g.C + g.D
592 })
593 const expected = 1_000_000 + 53 + 71 + 8
594 if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected {
595 t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected)
596 }
597 }
598 {
599 type OneLong struct{ a uintptr }
600 var TakeGoUintAndReturn func(a OneLong) uint64
601 register(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn", func(a OneLong) uint64 {
602 return uint64(a.a)
603 })
604 expected := uint64(7)
605 if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected {
606 t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected)
607 }
608 }
609 {
610 type FloatAndBool struct {
611 x float32
612 y bool
613 }
614 var FloatAndBoolFn func(FloatAndBool) int32
615 register(&FloatAndBoolFn, lib, "FloatAndBool", func(f FloatAndBool) int32 {
616 if f.y {
617 return 1
618 }
619 return 0
620 })
621 if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 {
622 t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret)
623 }
624 if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 {
625 t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret)
626 }
627 }
628 {
629 type FourInt32s struct {
630 f0, f1, f2, f3 int32
631 }
632 var FourInt32sFn func(FourInt32s) int32
633 register(&FourInt32sFn, lib, "FourInt32s", func(s FourInt32s) int32 {
634 return s.f0 + s.f1 + s.f2 + s.f3
635 })
636 result := FourInt32sFn(FourInt32s{100, -127, 4, -100})
637 const want = 100 - 127 + 4 - 100
638 if result != want {
639 t.Fatalf("FourInt32s returned %d wanted %d", result, want)
640 }
641 }
642 {
643 type PointerWrapper struct {
644 ctx unsafe.Pointer
645 }
646 var ExtractPointer func(wrapper PointerWrapper) uintptr
647 register(&ExtractPointer, lib, "ExtractPointer", func(wrapper PointerWrapper) uintptr {
648 return uintptr(wrapper.ctx)
649 })
650
651 var v int
652 ptr := unsafe.Pointer(&v)
653 expected := uintptr(ptr)
654 result := ExtractPointer(PointerWrapper{ctx: ptr})
655 if result != expected {
656 t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected)
657 }
658 }
659 {
660 type TwoPointers struct {
661 ptr1, ptr2 unsafe.Pointer
662 }
663 var AddPointers func(wrapper TwoPointers) uintptr
664 register(&AddPointers, lib, "AddPointers", func(wrapper TwoPointers) uintptr {
665 return uintptr(wrapper.ptr1) + uintptr(wrapper.ptr2)
666 })
667
668 var v1, v2 int
669 ptr1 := unsafe.Pointer(&v1)
670 ptr2 := unsafe.Pointer(&v2)
671 expected := uintptr(ptr1) + uintptr(ptr2)
672 result := AddPointers(TwoPointers{ptr1, ptr2})
673 if result != expected {
674 t.Fatalf("AddPointers returned %#x wanted %#x", result, expected)
675 }
676 }
677
678 // Identity struct arg tests
679 {
680 // Single int64: one integer register.
681 type OneInt64 struct{ A int64 }
682 var fn func(OneInt64) OneInt64
683 register(&fn, lib, "IdentityOneInt64", func(s OneInt64) OneInt64 {
684 return s
685 })
686 expected := OneInt64{0xDEADBEEF}
687 if ret := fn(expected); ret != expected {
688 t.Fatalf("IdentityOneInt64 returned %+v wanted %+v", ret, expected)
689 }
690 }
691 {
692 // Two int64 fields: two integer registers.
693 type IntLessThan16Bytes struct{ X, Y int64 }
694 var fn func(IntLessThan16Bytes) IntLessThan16Bytes
695 register(&fn, lib, "IdentityIntLessThan16Bytes", func(s IntLessThan16Bytes) IntLessThan16Bytes {
696 return s
697 })
698 expected := IntLessThan16Bytes{0xDEADBEEF, -42}
699 if ret := fn(expected); ret != expected {
700 t.Fatalf("IdentityIntLessThan16Bytes returned %+v wanted %+v", ret, expected)
701 }
702 }
703 {
704 // Two float64 fields: SSE on amd64, HFA on arm64.
705 type TwoDoubleStruct struct{ X, Y float64 }
706 var fn func(TwoDoubleStruct) TwoDoubleStruct
707 register(&fn, lib, "IdentityTwoDoubleStruct", func(s TwoDoubleStruct) TwoDoubleStruct {
708 return s
709 })
710 expected := TwoDoubleStruct{3.14, -2.71}
711 if ret := fn(expected); ret != expected {
712 t.Fatalf("IdentityTwoDoubleStruct returned %+v wanted %+v", ret, expected)
713 }
714 }
715 {
716 // Four float32 fields: HFA on arm64, SSE on amd64.
717 type FourFloat32 struct{ A, B, C, D float32 }
718 var fn func(FourFloat32) FourFloat32
719 register(&fn, lib, "IdentityFourFloat32", func(s FourFloat32) FourFloat32 {
720 return s
721 })
722 expected := FourFloat32{1.0, 2.0, 3.0, 4.0}
723 if ret := fn(expected); ret != expected {
724 t.Fatalf("IdentityFourFloat32 returned %+v wanted %+v", ret, expected)
725 }
726 }
727 {
728 // Mixed float32 + int32: INTEGER class eightbyte on amd64, non-HFA int regs on arm64.
729 type FloatAndInt struct {
730 X float32
731 Y int32
732 }
733 var fn func(FloatAndInt) FloatAndInt
734 register(&fn, lib, "IdentityFloatAndInt", func(s FloatAndInt) FloatAndInt {
735 return s
736 })
737 expected := FloatAndInt{7.5, 42}
738 if ret := fn(expected); ret != expected {
739 t.Fatalf("IdentityFloatAndInt returned %+v wanted %+v", ret, expected)
740 }
741 }
742 {
743 // Struct > 16 bytes: hidden pointer on amd64, pointer in int register on arm64.
744 type ThreeInt64 struct{ A, B, C int64 }
745 var fn func(ThreeInt64) ThreeInt64
746 register(&fn, lib, "IdentityThreeInt64", func(s ThreeInt64) ThreeInt64 {
747 return s
748 })
749 expected := ThreeInt64{1, 2, 3}
750 if ret := fn(expected); ret != expected {
751 t.Fatalf("IdentityThreeInt64 returned %+v wanted %+v", ret, expected)
752 }
753 }
754 {
755 // Struct > 16 bytes with pointer fields.
756 type PtrInt64Ptr struct {
757 A *int64
758 B int64
759 C *int64
760 }
761 a, c := new(int64), new(int64)
762 *a, *c = 100, 300
763 var fn func(PtrInt64Ptr) PtrInt64Ptr
764 register(&fn, lib, "IdentityPtrInt64Ptr", func(s PtrInt64Ptr) PtrInt64Ptr {
765 return s
766 })
767 expected := PtrInt64Ptr{a, 200, c}
768 ret := fn(expected)
769 if *ret.A != *expected.A || ret.B != expected.B || *ret.C != *expected.C {
770 t.Fatalf("IdentityPtrInt64Ptr returned %+v wanted %+v", ret, expected)
771 }
772 runtime.KeepAlive(a)
773 runtime.KeepAlive(c)
774 }
775 {
776 // Struct arg after some primitive args: register offset correctness.
777 type IntLessThan16Bytes struct{ X, Y int64 }
778 var fn func(int64, float64, IntLessThan16Bytes) IntLessThan16Bytes
779 register(&fn, lib, "IdentityTwoInt64AfterPrims", func(x int64, y float64, s IntLessThan16Bytes) IntLessThan16Bytes {
780 return s
781 })
782 expected := IntLessThan16Bytes{100, 200}
783 if ret := fn(1, 2.0, expected); ret != expected {
784 t.Fatalf("IdentityTwoInt64AfterPrims returned %+v wanted %+v", ret, expected)
785 }
786 }
787 {
788 // Float struct arg after float primitive args: float register offset.
789 type FloatLessThan16Bytes struct{ X, Y float32 }
790 var fn func(float64, float64, FloatLessThan16Bytes) FloatLessThan16Bytes
791 register(&fn, lib, "IdentityTwoFloat32AfterFloats", func(x, y float64, s FloatLessThan16Bytes) FloatLessThan16Bytes {
792 return s
793 })
794 expected := FloatLessThan16Bytes{5.0, 6.0}
795 if ret := fn(1.0, 2.0, expected); ret != expected {
796 t.Fatalf("IdentityTwoFloat32AfterFloats returned %+v wanted %+v", ret, expected)
797 }
798 }
799 {
800 // Mixed struct with pointer: pointer + int32 + float32 + int32.
801 type Mixed5Args struct {
802 A *int64
803 B int32
804 C float32
805 D int32
806 }
807 ptr := new(int64)
808 *ptr = 99
809 var fn func(Mixed5Args) Mixed5Args
810 register(&fn, lib, "IdentityMixed5Args", func(s Mixed5Args) Mixed5Args {
811 return s
812 })
813 expected := Mixed5Args{ptr, 1, 7.2, 9}
814 ret := fn(expected)
815 if ret.A != expected.A || ret.B != expected.B || ret.C != expected.C || ret.D != expected.D {
816 t.Fatalf("IdentityMixed5Args returned %+v wanted %+v", ret, expected)
817 }
818 runtime.KeepAlive(ptr)
819 }
820 })
821 }
822}
823
824// TODO: this could use the iter.Seq interface when purego supports Go 1.23
825func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) {
826 var fieldIndex int
827 var tracker func() (reflect.Value, bool)
828 return func() (reflect.Value, bool) {
829 if v.NumField() == 0 {
830 return reflect.Value{}, false
831 }
832 if tracker != nil {
833 if field, ok := tracker(); ok {
834 return field, ok
835 }
836 tracker = nil
837 }
838 for fieldIndex < v.NumField() {
839 if v.Type().Field(fieldIndex).Name == "_" {
840 fieldIndex++
841 continue
842 }
843 field := v.Field(fieldIndex)
844 fieldIndex++
845 if field.Kind() == reflect.Struct {
846 tracker = nextFieldFn(field)
847 if inner, ok := tracker(); ok {
848 return inner, ok
849 }
850 tracker = nil
851 } else {
852 return field, true
853 }
854 }
855 return reflect.Value{}, false
856 }
857}
858
859func TestRegisterFunc_structReturns(t *testing.T) {
860 libFileName := filepath.Join(t.TempDir(), "structreturntest.so")
861 t.Logf("Build %v", libFileName)
862
863 if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "structreturn_test.c")); err != nil {
864 t.Fatal(err)
865 }
866 defer os.Remove(libFileName)
867
868 lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
869 if err != nil {
870 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
871 }
872 implementations := []struct {
873 name string
874 register func(fptr any, handle uintptr, name string)
875 }{
876 {
877 name: "RegisterLibFunc",
878 register: purego.RegisterLibFunc,
879 },
880 {
881 name: "GoCallbackFunc",
882 register: func(fptr any, _ uintptr, _ string) {
883 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
884 retType := reflect.TypeOf(fptr).Elem().Out(0)
885 ret := reflect.New(retType).Elem()
886 next := nextFieldFn(ret)
887 for _, a := range args {
888 field, ok := next()
889 if !ok {
890 panic("purego: no more fields")
891 }
892 field.Set(a)
893 }
894 return []reflect.Value{ret}
895 })
896 purego.RegisterFunc(fptr, purego.NewCallback(fn.Interface()))
897 },
898 },
899 }
900 for _, imp := range implementations {
901 imp := imp
902 t.Run(imp.name, func(t *testing.T) {
903 register := imp.register
904 {
905 type Empty struct{}
906 var ReturnEmpty func() Empty
907 register(&ReturnEmpty, lib, "ReturnEmpty")
908 ret := ReturnEmpty()
909 _ = ret
910 }
911 {
912 type inner struct{ A int16 }
913 type StructInStruct struct {
914 A inner
915 B inner
916 C inner
917 }
918 var ReturnStructInStruct func(a, b, c int16) StructInStruct
919 register(&ReturnStructInStruct, lib, "ReturnStructInStruct")
920 expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}
921 if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {
922 t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)
923 }
924 }
925 {
926 type ThreeShorts struct{ A, B, C int16 }
927 var ReturnThreeShorts func(a, b, c int16) ThreeShorts
928 register(&ReturnThreeShorts, lib, "ReturnThreeShorts")
929 expected := ThreeShorts{^int16(0), 2, 3}
930 if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {
931 t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)
932 }
933 }
934 {
935 type FourShorts struct{ A, B, C, D int16 }
936 var ReturnFourShorts func(a, b, c, d int16) FourShorts
937 register(&ReturnFourShorts, lib, "ReturnFourShorts")
938 expected := FourShorts{^int16(0), 2, 3, 4}
939 if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {
940 t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)
941 }
942 }
943 {
944 type OneLong struct{ A int64 }
945 var ReturnOneLong func(a int64) OneLong
946 register(&ReturnOneLong, lib, "ReturnOneLong")
947 expected := OneLong{5}
948 if ret := ReturnOneLong(5); ret != expected {
949 t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)
950 }
951 }
952 {
953 type TwoLongs struct{ A, B int64 }
954 var ReturnTwoLongs func(a, b int64) TwoLongs
955 register(&ReturnTwoLongs, lib, "ReturnTwoLongs")
956 expected := TwoLongs{1, 2}
957 if ret := ReturnTwoLongs(1, 2); ret != expected {
958 t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)
959 }
960 }
961 {
962 type ThreeLongs struct{ A, B, C int64 }
963 var ReturnThreeLongs func(a, b, c int64) ThreeLongs
964 register(&ReturnThreeLongs, lib, "ReturnThreeLongs")
965 expected := ThreeLongs{1, 2, 3}
966 if ret := ReturnThreeLongs(1, 2, 3); ret != expected {
967 t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)
968 }
969 }
970 {
971 type OneFloat struct{ A float32 }
972 var ReturnOneFloat func(a float32) OneFloat
973 register(&ReturnOneFloat, lib, "ReturnOneFloat")
974 expected := OneFloat{1}
975 if ret := ReturnOneFloat(1); ret != expected {
976 t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)
977 }
978 }
979 {
980 type TwoFloats struct{ A, B float32 }
981 var ReturnTwoFloats func(a, b float32) TwoFloats
982 register(&ReturnTwoFloats, lib, "ReturnTwoFloats")
983 expected := TwoFloats{5, 2}
984 if ret := ReturnTwoFloats(5, 2); ret != expected {
985 t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)
986 }
987 }
988 {
989 type ThreeFloats struct{ A, B, C float32 }
990 var ReturnThreeFloats func(a, b, c float32) ThreeFloats
991 register(&ReturnThreeFloats, lib, "ReturnThreeFloats")
992 expected := ThreeFloats{1, 2, 3}
993 if ret := ReturnThreeFloats(1, 2, 3); ret != expected {
994 t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)
995 }
996 }
997 {
998 type FourFloats struct{ A, B, C, D float32 }
999 var ReturnFourFloats func(a, b, c, d float32) FourFloats
1000 register(&ReturnFourFloats, lib, "ReturnFourFloats")
1001 expected := FourFloats{1, 2, 3, 4}
1002 if ret := ReturnFourFloats(1, 2, 3, 4); ret != expected {
1003 t.Fatalf("ReturnFourFloats returned %+v wanted %+v", ret, expected)
1004 }
1005 }
1006 {
1007 type OneDouble struct{ A float64 }
1008 var ReturnOneDouble func(a float64) OneDouble
1009 register(&ReturnOneDouble, lib, "ReturnOneDouble")
1010 expected := OneDouble{1}
1011 if ret := ReturnOneDouble(1); ret != expected {
1012 t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)
1013 }
1014 }
1015 {
1016 type TwoDoubles struct{ A, B float64 }
1017 var ReturnTwoDoubles func(a, b float64) TwoDoubles
1018 register(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")
1019 expected := TwoDoubles{7, 3}
1020 if ret := ReturnTwoDoubles(7, 3); ret != expected {
1021 t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)
1022 }
1023 }
1024 {
1025 type ThreeDoubles struct{ A, B, C float64 }
1026 var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles
1027 register(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")
1028 expected := ThreeDoubles{1, 2, 3}
1029 if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {
1030 t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)
1031 }
1032 }
1033 {
1034 type FourDoubles struct{ A, B, C, D float64 }
1035 var ReturnFourDoubles func(a, b, c, d float64) FourDoubles
1036 register(&ReturnFourDoubles, lib, "ReturnFourDoubles")
1037 expected := FourDoubles{1, 2, 3, 4}
1038 if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {
1039 t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)
1040 }
1041 }
1042 {
1043 type FourDoublesInternal struct {
1044 F struct{ A, B float64 }
1045 G struct{ C, D float64 }
1046 }
1047 var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
1048 register(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
1049 expected := FourDoublesInternal{F: struct{ A, B float64 }{A: 1, B: 2}, G: struct{ C, D float64 }{C: 3, D: 4}}
1050 if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
1051 t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
1052 }
1053 }
1054 {
1055 type FiveDoubles struct{ A, B, C, D, E float64 }
1056 var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
1057 register(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
1058 expected := FiveDoubles{1, 2, 3, 4, 5}
1059 if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
1060 t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
1061 }
1062 }
1063 {
1064 type OneFloatOneDouble struct {
1065 A float32
1066 _ float32
1067 B float64
1068 }
1069 var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
1070 register(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
1071 expected := OneFloatOneDouble{A: 1, B: 2}
1072 if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
1073 t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
1074 }
1075 }
1076 {
1077 type OneDoubleOneFloat struct {
1078 A float64
1079 B float32
1080 }
1081 var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
1082 register(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
1083 expected := OneDoubleOneFloat{1, 2}
1084 if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
1085 t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
1086 }
1087 }
1088 {
1089 type Unaligned1 struct {
1090 A int8
1091 _ [1]int8
1092 B int16
1093 _ [1]int32
1094 C int64
1095 }
1096 var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
1097 register(&ReturnUnaligned1, lib, "ReturnUnaligned1")
1098 expected := Unaligned1{A: 1, B: 2, C: 3}
1099 if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
1100 t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
1101 }
1102 }
1103
1104 {
1105 type Mixed1 struct {
1106 A float32
1107 B int32
1108 }
1109 var ReturnMixed1 func(a float32, b int32) Mixed1
1110 register(&ReturnMixed1, lib, "ReturnMixed1")
1111 expected := Mixed1{1, 2}
1112 if ret := ReturnMixed1(1, 2); ret != expected {
1113 t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
1114 }
1115 }
1116 {
1117 type Mixed2 struct {
1118 A float32
1119 B int32
1120 C float32
1121 D int32
1122 }
1123 var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
1124 register(&ReturnMixed2, lib, "ReturnMixed2")
1125 expected := Mixed2{1, 2, 3, 4}
1126 if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
1127 t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
1128 }
1129 }
1130 {
1131 type Mixed3 struct {
1132 A float32
1133 B uint32
1134 C float64
1135 }
1136 var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
1137 register(&ReturnMixed3, lib, "ReturnMixed3")
1138 expected := Mixed3{1, 2, 3}
1139 if ret := ReturnMixed3(1, 2, 3); ret != expected {
1140 t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
1141 }
1142 }
1143 {
1144 type Mixed4 struct {
1145 A float64
1146 B uint32
1147 C float32
1148 }
1149 var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
1150 register(&ReturnMixed4, lib, "ReturnMixed4")
1151 expected := Mixed4{1, 2, 3}
1152 if ret := ReturnMixed4(1, 2, 3); ret != expected {
1153 t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
1154 }
1155 }
1156 {
1157 type Mixed5 struct {
1158 A *int64
1159 B int32
1160 C float32
1161 D int32
1162 }
1163 var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
1164 register(&ReturnMixed5, lib, "ReturnMixed5")
1165 ptr := new(int64)
1166 expected := Mixed5{ptr, 1, 7.2, 9}
1167 if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
1168 t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
1169 }
1170 runtime.KeepAlive(ptr)
1171 }
1172 {
1173 type Mixed5 struct {
1174 A *int64
1175 B int32
1176 C float32
1177 D int32
1178 }
1179 var IdentityMixed5 func(m Mixed5) Mixed5
1180 // The GoCallbackFunc register helper decomposes args into struct fields,
1181 // which doesn't work for identity functions with struct args.
1182 // Struct callback args are tested in TestRegisterFunc_structArgs.
1183 purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
1184 ptr := new(int64)
1185 expected := Mixed5{ptr, 1, 7.2, 9}
1186 if ret := IdentityMixed5(expected); ret != expected {
1187 t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
1188 }
1189 runtime.KeepAlive(ptr)
1190 }
1191 {
1192 type SmallBool struct {
1193 A bool
1194 B int32
1195 C int64
1196 }
1197 var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
1198 register(&ReturnSmallBool, lib, "ReturnSmallBool")
1199 expected := SmallBool{true, 42, 123456789}
1200 if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
1201 t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
1202 }
1203 }
1204 {
1205 type LargeBool struct {
1206 A bool
1207 B int32
1208 C int64
1209 D int64
1210 }
1211 var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
1212 register(&ReturnLargeBool, lib, "ReturnLargeBool")
1213 expected := LargeBool{false, -99, 987654321, 111222333444}
1214 if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
1215 t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
1216 }
1217 }
1218 {
1219 type Ptr1 struct {
1220 a *int64
1221 b unsafe.Pointer
1222 }
1223 var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
1224 purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
1225 a, b := new(int64), new(struct{})
1226 expected := Ptr1{a, unsafe.Pointer(b)}
1227 if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
1228 t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
1229 }
1230 runtime.KeepAlive(a)
1231 runtime.KeepAlive(b)
1232 }
1233 })
1234 }
1235}