v0.12.0-alpha 7e3b362

struct_test.go RawHistoryFolder

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

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