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/bnema/purego"
21 "github.com/bnema/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}