v0.11.0-alpha.6 d60df56

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)
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}

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