v0.11.0-alpha.5 5faf54b

struct_test.go RawHistoryFolder

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

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