v0.11.0-alpha.9 f9d41fc

struct_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close