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