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