1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3
4//go:build (darwin || linux) && (amd64 || arm64)
5
6package purego_test
7
8import (
9 "math"
10 "os"
11 "path/filepath"
12 "runtime"
13 "testing"
14 "unsafe"
15
16 "github.com/ebitengine/purego"
17)
18
19func TestRegisterFunc_structArgs(t *testing.T) {
20 libFileName := filepath.Join(t.TempDir(), "structtest.so")
21 t.Logf("Build %v", libFileName)
22
23 if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "struct_test.c")); err != nil {
24 t.Fatal(err)
25 }
26 defer os.Remove(libFileName)
27
28 lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
29 if err != nil {
30 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
31 }
32
33 const (
34 expectedUnsigned = 0xdeadbeef
35 expectedSigned = -123
36 expectedOdd = 12 + 23 + 46
37 expectedLong uint64 = 0xdeadbeefcafebabe
38 expectedFloat float32 = 10
39 expectedDouble float64 = 10
40 )
41
42 {
43 type Empty struct{}
44 var NoStruct func(Empty) int64
45 purego.RegisterLibFunc(&NoStruct, lib, "NoStruct")
46 if ret := NoStruct(Empty{}); ret != expectedUnsigned {
47 t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned)
48 }
49 }
50 {
51 type EmptyEmpty struct{}
52 var EmptyEmptyFn func(EmptyEmpty) int64
53 purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty")
54 if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned {
55 t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned)
56 }
57 var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64
58 purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg")
59 if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned {
60 t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned)
61 }
62 }
63 {
64 type GreaterThan16Bytes struct {
65 x, y, z *int64
66 }
67 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
68 var GreaterThan16BytesFn func(GreaterThan16Bytes) int64
69 purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes")
70 if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
71 t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned)
72 }
73 }
74 {
75 type GreaterThan16BytesStruct struct {
76 a struct {
77 x, y, z *int64
78 }
79 }
80 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
81 var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64
82 purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct")
83 if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned {
84 t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
85 }
86 }
87 {
88 type GreaterThan16Bytes struct {
89 x, y, z *int64
90 }
91 var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
92 var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64
93 purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters")
94 if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
95 t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned)
96 }
97 var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64
98 z -= 0xFF
99 purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters")
100 if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned {
101 t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned)
102 }
103 }
104 {
105 type IntLessThan16Bytes struct {
106 x, y int64
107 }
108 var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64
109 purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes")
110 if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned {
111 t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
112 }
113 }
114 {
115 type FloatLessThan16Bytes struct {
116 x, y float32
117 }
118 var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32
119 purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes")
120 if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat {
121 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
122 }
123 }
124 {
125 type ThreeSmallFields struct {
126 x, y, z float32
127 }
128 var ThreeSmallFieldsFn func(ThreeSmallFields) float32
129 purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields")
130 if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
131 t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
132 }
133 }
134 {
135 type FloatAndInt struct {
136 x float32
137 y int32
138 }
139 var FloatAndIntFn func(FloatAndInt) float32
140 purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt")
141 if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat {
142 t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
143 }
144 }
145 {
146 type DoubleStruct struct {
147 x float64
148 }
149 var DoubleStructFn func(DoubleStruct) float64
150 purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct")
151 if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
152 t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble)
153 }
154 }
155 {
156 type TwoDoubleStruct struct {
157 x, y float64
158 }
159 var TwoDoubleStructFn func(TwoDoubleStruct) float64
160 purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct")
161 if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble {
162 t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble)
163 }
164 }
165 {
166 type TwoDoubleTwoStruct struct {
167 x struct {
168 x, y float64
169 }
170 }
171 var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64
172 purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct")
173 if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble {
174 t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble)
175 }
176 }
177 {
178 type ThreeDoubleStruct struct {
179 x, y, z float64
180 }
181 var ThreeDoubleStructFn func(ThreeDoubleStruct) float64
182 purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct")
183 if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble {
184 t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble)
185 }
186 }
187 {
188 type LargeFloatStruct struct {
189 a, b, c, d, e, f float64
190 }
191 var LargeFloatStructFn func(LargeFloatStruct) float64
192 purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct")
193 if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
194 t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
195 }
196 var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64
197 purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs")
198 if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
199 t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat)
200 }
201 }
202 {
203 type Rect struct {
204 x, y, w, h float64
205 }
206 var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64
207 purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs")
208 if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble {
209 t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble)
210 }
211 var RectangleSubtract func(rect Rect) float64
212 purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract")
213 if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble {
214 t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble)
215 }
216 var Rectangle func(rect Rect) float64
217 purego.RegisterLibFunc(&Rectangle, lib, "Rectangle")
218 if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
219 t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat)
220 }
221 }
222 {
223 type FloatArray struct {
224 a [2]float64
225 }
226 var FloatArrayFn func(rect FloatArray) float64
227 purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray")
228 if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble {
229 t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat)
230 }
231 }
232 {
233 type UnsignedChar4Bytes struct {
234 a, b, c, d byte
235 }
236 var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
237 purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes")
238 if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned {
239 t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
240 }
241 }
242 {
243 type UnsignedChar4BytesStruct struct {
244 x struct {
245 a byte
246 }
247 y struct {
248 b byte
249 }
250 z struct {
251 c byte
252 }
253 w struct {
254 d byte
255 }
256 }
257 var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32
258 purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct")
259 if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{
260 x: struct{ a byte }{a: 0xDE},
261 y: struct{ b byte }{b: 0xAD},
262 z: struct{ c byte }{c: 0xBE},
263 w: struct{ d byte }{d: 0xEF},
264 }); ret != expectedUnsigned {
265 t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
266 }
267 }
268 {
269 type Short struct {
270 a, b, c, d uint16
271 }
272 var ShortFn func(Short) uint64
273 purego.RegisterLibFunc(&ShortFn, lib, "Short")
274 if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
275 t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
276 }
277 }
278 {
279 type Int struct {
280 a, b uint32
281 }
282 var IntFn func(Int) uint64
283 purego.RegisterLibFunc(&IntFn, lib, "Int")
284 if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong {
285 t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong)
286 }
287 }
288 {
289 type Long struct {
290 a uint64
291 }
292 var LongFn func(Long) uint64
293 purego.RegisterLibFunc(&LongFn, lib, "Long")
294 if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong {
295 t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong)
296 }
297 }
298 {
299 type Char8Bytes struct {
300 a, b, c, d, e, f, g, h int8
301 }
302 var Char8BytesFn func(Char8Bytes) int32
303 purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes")
304 if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned {
305 t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned)
306 }
307 }
308 {
309 type Odd struct {
310 a, b, c byte
311 }
312 var OddFn func(Odd) int32
313 purego.RegisterLibFunc(&OddFn, lib, "Odd")
314 if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd {
315 t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd)
316 }
317 }
318 {
319 type Char2Short1 struct {
320 a, b byte
321 c uint16
322 }
323 var Char2Short1s func(Char2Short1) int32
324 purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s")
325 if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd {
326 t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd)
327 }
328 }
329 {
330 type SignedChar2Short1 struct {
331 a, b int8
332 c int16
333 }
334 var SignedChar2Short1Fn func(SignedChar2Short1) int32
335 purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1")
336 if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned {
337 t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned)
338 }
339 }
340 {
341 type Array4UnsignedChars struct {
342 a [4]uint8
343 }
344 var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32
345 purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars")
346 if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned {
347 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
348 }
349 }
350 {
351 type Array3UnsignedChar struct {
352 a [3]uint8
353 }
354 var Array3UnsignedChars func(chars Array3UnsignedChar) uint32
355 purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars")
356 if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned {
357 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
358 }
359 }
360 {
361 type Array2UnsignedShort struct {
362 a [2]uint16
363 }
364 var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32
365 purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts")
366 if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned {
367 t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
368 }
369 }
370 {
371 type Array4Chars struct {
372 a [4]int8
373 }
374 var Array4CharsFn func(chars Array4Chars) int32
375 purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
376 const expectedSum = 10 + 20 + 30 + 63
377 if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum {
378 t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum)
379 }
380 }
381 {
382 type Array2Short struct {
383 a [2]int16
384 }
385 var Array2Shorts func(chars Array2Short) int32
386 purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts")
387 if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned {
388 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
389 }
390 }
391 {
392 type Array3Short struct {
393 a [3]int16
394 }
395 var Array3Shorts func(chars Array3Short) int32
396 purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts")
397 if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned {
398 t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
399 }
400 }
401 {
402 type BoolStruct struct {
403 b bool
404 }
405 var BoolStructFn func(BoolStruct) bool
406 purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct")
407 if ret := BoolStructFn(BoolStruct{true}); ret != true {
408 t.Fatalf("BoolStructFn returned %v wanted %v", ret, true)
409 }
410 if ret := BoolStructFn(BoolStruct{false}); ret != false {
411 t.Fatalf("BoolStructFn returned %v wanted %v", ret, false)
412 }
413 }
414 {
415 type BoolFloat struct {
416 b bool
417 _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct
418 f float32
419 }
420 var BoolFloatFn func(BoolFloat) float32
421 purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat")
422 if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat {
423 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat)
424 }
425 if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat {
426 t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat)
427 }
428 }
429 {
430 type point struct{ x, y float64 }
431 type size struct{ width, height float64 }
432 type Content struct {
433 point point
434 size size
435 }
436 var InitWithContentRect func(*int, Content, int32, int32, bool) uint64
437 purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect")
438 if ret := InitWithContentRect(new(int),
439 // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef
440 Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}},
441 15, 4, true); ret != expectedUnsigned {
442 t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned)
443 }
444 }
445 {
446 type GoInt4 struct {
447 A, B, C, D int
448 }
449 var GoInt4Fn func(GoInt4) int
450 purego.RegisterLibFunc(&GoInt4Fn, lib, "GoInt4")
451 const expected = math.MaxInt - 52 - 3 + 4
452 if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected {
453 t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected)
454 }
455 }
456 {
457 type GoUint4 struct {
458 A, B, C, D uint
459 }
460 var GoUint4Fn func(GoUint4) uint
461 purego.RegisterLibFunc(&GoUint4Fn, lib, "GoUint4")
462 const expected = 1_000_000 + 53 + 71 + 8
463 if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected {
464 t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected)
465 }
466 }
467 {
468 type OneLong struct{ a uintptr }
469 var TakeGoUintAndReturn func(a OneLong) uint64
470 purego.RegisterLibFunc(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn")
471 expected := uint64(7)
472 if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected {
473 t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected)
474 }
475 }
476 {
477 type FloatAndBool struct {
478 x float32
479 y bool
480 }
481 var FloatAndBoolFn func(FloatAndBool) int32
482 purego.RegisterLibFunc(&FloatAndBoolFn, lib, "FloatAndBool")
483 if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 {
484 t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret)
485 }
486 if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 {
487 t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret)
488 }
489 }
490 {
491 type FourInt32s struct {
492 f0, f1, f2, f3 int32
493 }
494 var FourInt32sFn func(FourInt32s) int32
495 purego.RegisterLibFunc(&FourInt32sFn, lib, "FourInt32s")
496 result := FourInt32sFn(FourInt32s{100, -127, 4, -100})
497 const want = 100 - 127 + 4 - 100
498 if result != want {
499 t.Fatalf("FourInt32s returned %d wanted %d", result, want)
500 }
501 }
502 {
503 type PointerWrapper struct {
504 ctx unsafe.Pointer
505 }
506 var ExtractPointer func(wrapper PointerWrapper) uintptr
507 purego.RegisterLibFunc(&ExtractPointer, lib, "ExtractPointer")
508
509 var v int
510 ptr := unsafe.Pointer(&v)
511 expected := uintptr(ptr)
512 result := ExtractPointer(PointerWrapper{ctx: ptr})
513 if result != expected {
514 t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected)
515 }
516 }
517 {
518 type TwoPointers struct {
519 ptr1, ptr2 unsafe.Pointer
520 }
521 var AddPointers func(wrapper TwoPointers) uintptr
522 purego.RegisterLibFunc(&AddPointers, lib, "AddPointers")
523
524 var v1, v2 int
525 ptr1 := unsafe.Pointer(&v1)
526 ptr2 := unsafe.Pointer(&v2)
527 expected := uintptr(ptr1) + uintptr(ptr2)
528 result := AddPointers(TwoPointers{ptr1, ptr2})
529 if result != expected {
530 t.Fatalf("AddPointers returned %#x wanted %#x", result, expected)
531 }
532 }
533}
534
535func TestRegisterFunc_structReturns(t *testing.T) {
536 libFileName := filepath.Join(t.TempDir(), "structreturntest.so")
537 t.Logf("Build %v", libFileName)
538
539 if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "structreturn_test.c")); err != nil {
540 t.Fatal(err)
541 }
542 defer os.Remove(libFileName)
543
544 lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
545 if err != nil {
546 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
547 }
548
549 {
550 type Empty struct{}
551 var ReturnEmpty func() Empty
552 purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty")
553 ret := ReturnEmpty()
554 _ = ret
555 }
556 {
557 type inner struct{ a int16 }
558 type StructInStruct struct {
559 a inner
560 b inner
561 c inner
562 }
563 var ReturnStructInStruct func(a, b, c int16) StructInStruct
564 purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct")
565 expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}
566 if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {
567 t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)
568 }
569 }
570 {
571 type ThreeShorts struct{ a, b, c int16 }
572 var ReturnThreeShorts func(a, b, c int16) ThreeShorts
573 purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts")
574 expected := ThreeShorts{^int16(0), 2, 3}
575 if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {
576 t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)
577 }
578 }
579 {
580 type FourShorts struct{ a, b, c, d int16 }
581 var ReturnFourShorts func(a, b, c, d int16) FourShorts
582 purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts")
583 expected := FourShorts{^int16(0), 2, 3, 4}
584 if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {
585 t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)
586 }
587 }
588 {
589 type OneLong struct{ a int64 }
590 var ReturnOneLong func(a int64) OneLong
591 purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong")
592 expected := OneLong{5}
593 if ret := ReturnOneLong(5); ret != expected {
594 t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)
595 }
596 }
597 {
598 type TwoLongs struct{ a, b int64 }
599 var ReturnTwoLongs func(a, b int64) TwoLongs
600 purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs")
601 expected := TwoLongs{1, 2}
602 if ret := ReturnTwoLongs(1, 2); ret != expected {
603 t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)
604 }
605 }
606 {
607 type ThreeLongs struct{ a, b, c int64 }
608 var ReturnThreeLongs func(a, b, c int64) ThreeLongs
609 purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs")
610 expected := ThreeLongs{1, 2, 3}
611 if ret := ReturnThreeLongs(1, 2, 3); ret != expected {
612 t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)
613 }
614 }
615 {
616 type OneFloat struct{ a float32 }
617 var ReturnOneFloat func(a float32) OneFloat
618 purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat")
619 expected := OneFloat{1}
620 if ret := ReturnOneFloat(1); ret != expected {
621 t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)
622 }
623 }
624 {
625 type TwoFloats struct{ a, b float32 }
626 var ReturnTwoFloats func(a, b float32) TwoFloats
627 purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats")
628 expected := TwoFloats{3, 10}
629 if ret := ReturnTwoFloats(5, 2); ret != expected {
630 t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)
631 }
632 }
633 {
634 type ThreeFloats struct{ a, b, c float32 }
635 var ReturnThreeFloats func(a, b, c float32) ThreeFloats
636 purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats")
637 expected := ThreeFloats{1, 2, 3}
638 if ret := ReturnThreeFloats(1, 2, 3); ret != expected {
639 t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)
640 }
641 }
642 {
643 type OneDouble struct{ a float64 }
644 var ReturnOneDouble func(a float64) OneDouble
645 purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble")
646 expected := OneDouble{1}
647 if ret := ReturnOneDouble(1); ret != expected {
648 t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)
649 }
650 }
651 {
652 type TwoDoubles struct{ a, b float64 }
653 var ReturnTwoDoubles func(a, b float64) TwoDoubles
654 purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")
655 expected := TwoDoubles{1, 2}
656 if ret := ReturnTwoDoubles(1, 2); ret != expected {
657 t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)
658 }
659 }
660 {
661 type ThreeDoubles struct{ a, b, c float64 }
662 var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles
663 purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")
664 expected := ThreeDoubles{1, 2, 3}
665 if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {
666 t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)
667 }
668 }
669 {
670 type FourDoubles struct{ a, b, c, d float64 }
671 var ReturnFourDoubles func(a, b, c, d float64) FourDoubles
672 purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles")
673 expected := FourDoubles{1, 2, 3, 4}
674 if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {
675 t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)
676 }
677 }
678 {
679 type FourDoublesInternal struct {
680 f struct{ a, b float64 }
681 g struct{ c, d float64 }
682 }
683 var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
684 purego.RegisterLibFunc(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
685 expected := FourDoublesInternal{f: struct{ a, b float64 }{a: 1, b: 2}, g: struct{ c, d float64 }{c: 3, d: 4}}
686 if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
687 t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
688 }
689 }
690 {
691 type FiveDoubles struct{ a, b, c, d, e float64 }
692 var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
693 purego.RegisterLibFunc(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
694 expected := FiveDoubles{1, 2, 3, 4, 5}
695 if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
696 t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
697 }
698 }
699 {
700 type OneFloatOneDouble struct {
701 a float32
702 _ float32
703 b float64
704 }
705 var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
706 purego.RegisterLibFunc(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
707 expected := OneFloatOneDouble{a: 1, b: 2}
708 if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
709 t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
710 }
711 }
712 {
713 type OneDoubleOneFloat struct {
714 a float64
715 b float32
716 }
717 var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
718 purego.RegisterLibFunc(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
719 expected := OneDoubleOneFloat{1, 2}
720 if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
721 t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
722 }
723 }
724 {
725 type Unaligned1 struct {
726 a int8
727 _ [1]int8
728 b int16
729 _ [1]int32
730 c int64
731 }
732 var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
733 purego.RegisterLibFunc(&ReturnUnaligned1, lib, "ReturnUnaligned1")
734 expected := Unaligned1{a: 1, b: 2, c: 3}
735 if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
736 t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
737 }
738 }
739 {
740 type Mixed1 struct {
741 a float32
742 b int32
743 }
744 var ReturnMixed1 func(a float32, b int32) Mixed1
745 purego.RegisterLibFunc(&ReturnMixed1, lib, "ReturnMixed1")
746 expected := Mixed1{1, 2}
747 if ret := ReturnMixed1(1, 2); ret != expected {
748 t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
749 }
750 }
751 {
752 type Mixed2 struct {
753 a float32
754 b int32
755 c float32
756 d int32
757 }
758 var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
759 purego.RegisterLibFunc(&ReturnMixed2, lib, "ReturnMixed2")
760 expected := Mixed2{1, 2, 3, 4}
761 if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
762 t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
763 }
764 }
765 {
766 type Mixed3 struct {
767 a float32
768 b uint32
769 c float64
770 }
771 var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
772 purego.RegisterLibFunc(&ReturnMixed3, lib, "ReturnMixed3")
773 expected := Mixed3{1, 2, 3}
774 if ret := ReturnMixed3(1, 2, 3); ret != expected {
775 t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
776 }
777 }
778 {
779 type Mixed4 struct {
780 a float64
781 b uint32
782 c float32
783 }
784 var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
785 purego.RegisterLibFunc(&ReturnMixed4, lib, "ReturnMixed4")
786 expected := Mixed4{1, 2, 3}
787 if ret := ReturnMixed4(1, 2, 3); ret != expected {
788 t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
789 }
790 }
791 {
792 type Mixed5 struct {
793 a *int64
794 b int32
795 c float32
796 d int32
797 }
798 var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
799 purego.RegisterLibFunc(&ReturnMixed5, lib, "ReturnMixed5")
800 ptr := new(int64)
801 expected := Mixed5{ptr, 1, 7.2, 9}
802 if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
803 t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
804 }
805 runtime.KeepAlive(ptr)
806 }
807 {
808 type Mixed5 struct {
809 a *int64
810 b int32
811 c float32
812 d int32
813 }
814 var IdentityMixed5 func(m Mixed5) Mixed5
815 purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
816 ptr := new(int64)
817 expected := Mixed5{ptr, 1, 7.2, 9}
818 if ret := IdentityMixed5(expected); ret != expected {
819 t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
820 }
821 runtime.KeepAlive(ptr)
822 }
823 {
824 type SmallBool struct {
825 a bool
826 b int32
827 c int64
828 }
829 var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
830 purego.RegisterLibFunc(&ReturnSmallBool, lib, "ReturnSmallBool")
831 expected := SmallBool{true, 42, 123456789}
832 if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
833 t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
834 }
835 }
836 {
837 type LargeBool struct {
838 a bool
839 b int32
840 c int64
841 d int64
842 }
843 var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
844 purego.RegisterLibFunc(&ReturnLargeBool, lib, "ReturnLargeBool")
845 expected := LargeBool{false, -99, 987654321, 111222333444}
846 if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
847 t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
848 }
849 }
850 {
851 type Ptr1 struct {
852 a *int64
853 b unsafe.Pointer
854 }
855 var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
856 purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
857 a, b := new(int64), new(struct{})
858 expected := Ptr1{a, unsafe.Pointer(b)}
859 if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
860 t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
861 }
862 runtime.KeepAlive(a)
863 runtime.KeepAlive(b)
864 }
865}