purego: support returning structs from callbacks (#436)
Add setStruct implementations for all architectures to enable callbacks created with NewCallback to return struct values. Updates #225 Co-authored-by: TotallyGamerJet <TotallyGamerJet@users.noreply.github.com>
- commit
- 6df3e6fdbb63f831bb1496a9dae6521a45729c67
- author
- Hajime Hoshi · · 2026-03-10 03:36 UTC
- committer
- GitHub · · 2026-03-10 03:36 UTC
- parent
- 81d8c41
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown
18 files · +617 -351
M func.go +5 -5
| @@ -173,7 +173,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 173 | 173 | stack++ | |
| 174 | 174 | } | |
| 175 | 175 | case reflect.Struct: | |
| 176 | - | ensureStructSupportedForRegisterFunc() | |
| 176 | + | ensureStructSupported() | |
| 177 | 177 | if arg.Size() == 0 { | |
| 178 | 178 | continue | |
| 179 | 179 | } | |
| @@ -192,7 +192,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 192 | 192 | } | |
| 193 | 193 | } | |
| 194 | 194 | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | |
| 195 | - | ensureStructSupportedForRegisterFunc() | |
| 195 | + | ensureStructSupported() | |
| 196 | 196 | outType := ty.Out(0) | |
| 197 | 197 | checkStructFieldsSupported(outType) | |
| 198 | 198 | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | |
| @@ -490,12 +490,12 @@ func checkStructFieldsSupported(ty reflect.Type) { | |||
| 490 | 490 | } | |
| 491 | 491 | } | |
| 492 | 492 | ||
| 493 | - | func ensureStructSupportedForRegisterFunc() { | |
| 493 | + | func ensureStructSupported() { | |
| 494 | 494 | if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { | |
| 495 | - | panic("purego: struct arguments are only supported on amd64 and arm64") | |
| 495 | + | panic("purego: struct arguments/returns are only supported on amd64 and arm64") | |
| 496 | 496 | } | |
| 497 | 497 | if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { | |
| 498 | - | panic("purego: struct arguments are only supported on darwin and linux") | |
| 498 | + | panic("purego: struct arguments/returns are only supported on darwin and linux") | |
| 499 | 499 | } | |
| 500 | 500 | } | |
| 501 | 501 | ||
| @@ -173,7 +173,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 173 | stack++ | 173 | stack++ | ||
| 174 | } | 174 | } | ||
| 175 | case reflect.Struct: | 175 | case reflect.Struct: | ||
| 176 | - | ensureStructSupportedForRegisterFunc() | 176 | + | ensureStructSupported() |
| 177 | if arg.Size() == 0 { | 177 | if arg.Size() == 0 { | ||
| 178 | continue | 178 | continue | ||
| 179 | } | 179 | } | ||
| @@ -192,7 +192,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 192 | } | 192 | } | ||
| 193 | } | 193 | } | ||
| 194 | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | 194 | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | ||
| 195 | - | ensureStructSupportedForRegisterFunc() | 195 | + | ensureStructSupported() |
| 196 | outType := ty.Out(0) | 196 | outType := ty.Out(0) | ||
| 197 | checkStructFieldsSupported(outType) | 197 | checkStructFieldsSupported(outType) | ||
| 198 | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | 198 | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | ||
| @@ -490,12 +490,12 @@ func checkStructFieldsSupported(ty reflect.Type) { | |||||
| 490 | } | 490 | } | ||
| 491 | } | 491 | } | ||
| 492 | 492 | ||||
| 493 | - | func ensureStructSupportedForRegisterFunc() { | 493 | + | func ensureStructSupported() { |
| 494 | if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { | 494 | if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { | ||
| 495 | - | panic("purego: struct arguments are only supported on amd64 and arm64") | 495 | + | panic("purego: struct arguments/returns are only supported on amd64 and arm64") |
| 496 | } | 496 | } | ||
| 497 | if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { | 497 | if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { | ||
| 498 | - | panic("purego: struct arguments are only supported on darwin and linux") | 498 | + | panic("purego: struct arguments/returns are only supported on darwin and linux") |
| 499 | } | 499 | } | ||
| 500 | } | 500 | } | ||
| 501 | 501 | ||||
M objc/objc_block_darwin_test.go +1 -1
| @@ -59,7 +59,7 @@ func ExampleInvokeBlock() { | |||
| 59 | 59 | &vector{X: 6.7, Y: 8.9, Z: 0.1}, | |
| 60 | 60 | ) | |
| 61 | 61 | ||
| 62 | - | fmt.Println(*result, err) | |
| 62 | + | fmt.Printf("{%.2f %.2f %.2f} %v\n", result.X, result.Y, result.Z, err) | |
| 63 | 63 | // Output: {-39.82 30.14 -14.52} <nil> | |
| 64 | 64 | } | |
| 65 | 65 | ||
| @@ -59,7 +59,7 @@ func ExampleInvokeBlock() { | |||||
| 59 | &vector{X: 6.7, Y: 8.9, Z: 0.1}, | 59 | &vector{X: 6.7, Y: 8.9, Z: 0.1}, | ||
| 60 | ) | 60 | ) | ||
| 61 | 61 | ||||
| 62 | - | fmt.Println(*result, err) | 62 | + | fmt.Printf("{%.2f %.2f %.2f} %v\n", result.X, result.Y, result.Z, err) |
| 63 | // Output: {-39.82 30.14 -14.52} <nil> | 63 | // Output: {-39.82 30.14 -14.52} <nil> | ||
| 64 | } | 64 | } | ||
| 65 | 65 | ||||
M struct_386.go +4 -0
| @@ -39,3 +39,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||
| 39 | 39 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 40 | 40 | panic("purego: bundleStackArgs should not be called on 386") | |
| 41 | 41 | } | |
| 42 | + | ||
| 43 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 44 | + | panic("purego: struct returns are not supported on 386") | |
| 45 | + | } | |
| @@ -39,3 +39,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||||
| 39 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 39 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 40 | panic("purego: bundleStackArgs should not be called on 386") | 40 | panic("purego: bundleStackArgs should not be called on 386") | ||
| 41 | } | 41 | } | ||
| 42 | + | ||||
| 43 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 44 | + | panic("purego: struct returns are not supported on 386") | |||
| 45 | + | } | |||
M struct_amd64.go +84 -0
| @@ -284,3 +284,87 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||
| 284 | 284 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 285 | 285 | panic("purego: bundleStackArgs should not be called on amd64") | |
| 286 | 286 | } | |
| 287 | + | ||
| 288 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 289 | + | outSize := ret.Type().Size() | |
| 290 | + | switch { | |
| 291 | + | case outSize == 0: | |
| 292 | + | return | |
| 293 | + | case outSize <= 16: | |
| 294 | + | // Copy the struct's raw bytes (including padding) into a buffer. | |
| 295 | + | var buf [2]uintptr | |
| 296 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&buf[0])).Elem().Set(ret) | |
| 297 | + | // Classify each eightbyte by the SysV ABI rules (§3.2.3, rule 4d | |
| 298 | + | // of https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf). | |
| 299 | + | // INTEGER wins over SSE for mixed eightbytes. Place INTEGER eightbytes in | |
| 300 | + | // result[0]/result[1] (AX/DX) and SSE eightbytes in result[2]/result[3] | |
| 301 | + | // (XMM0/XMM1). | |
| 302 | + | // Assign each eightbyte to the next available register of the | |
| 303 | + | // appropriate class. The ABI counts integer (AX, DX) and SSE | |
| 304 | + | // (XMM0, XMM1) return registers independently. | |
| 305 | + | var numInts int | |
| 306 | + | var numFloats int | |
| 307 | + | for i := 0; i < 2 && uintptr(i)*8 < outSize; i++ { | |
| 308 | + | class := classifyEightbyte(ret.Type(), uintptr(i)*8, uintptr(i)*8+8) | |
| 309 | + | if class == _SSE { | |
| 310 | + | switch numFloats { | |
| 311 | + | case 0: | |
| 312 | + | a.result[2] = buf[i] | |
| 313 | + | case 1: | |
| 314 | + | a.result[3] = buf[i] | |
| 315 | + | } | |
| 316 | + | numFloats++ | |
| 317 | + | } else { | |
| 318 | + | switch numInts { | |
| 319 | + | case 0: | |
| 320 | + | a.result[0] = buf[i] | |
| 321 | + | case 1: | |
| 322 | + | a.result[1] = buf[i] | |
| 323 | + | } | |
| 324 | + | numInts++ | |
| 325 | + | } | |
| 326 | + | } | |
| 327 | + | default: | |
| 328 | + | // Structs > 16 bytes are returned by hidden pointer. | |
| 329 | + | // a.result[0] contains the pointer passed by the caller in RDI. | |
| 330 | + | // Write the struct through this pointer. | |
| 331 | + | reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret) | |
| 332 | + | } | |
| 333 | + | } | |
| 334 | + | ||
| 335 | + | // classifyEightbyte returns the SysV ABI class for the byte range [start, end) | |
| 336 | + | // within a type, by examining all scalar fields that overlap that range. | |
| 337 | + | func classifyEightbyte(t reflect.Type, start, end uintptr) int { | |
| 338 | + | return doClassifyEightbyte(t, 0, start, end) | |
| 339 | + | } | |
| 340 | + | ||
| 341 | + | func doClassifyEightbyte(t reflect.Type, base, start, end uintptr) int { | |
| 342 | + | switch t.Kind() { | |
| 343 | + | case reflect.Struct: | |
| 344 | + | class := _NO_CLASS | |
| 345 | + | for i := 0; i < t.NumField(); i++ { | |
| 346 | + | f := t.Field(i) | |
| 347 | + | class |= doClassifyEightbyte(f.Type, base+f.Offset, start, end) | |
| 348 | + | } | |
| 349 | + | return class | |
| 350 | + | case reflect.Array: | |
| 351 | + | class := _NO_CLASS | |
| 352 | + | elemSize := t.Elem().Size() | |
| 353 | + | for i := 0; i < t.Len(); i++ { | |
| 354 | + | class |= doClassifyEightbyte(t.Elem(), base+uintptr(i)*elemSize, start, end) | |
| 355 | + | } | |
| 356 | + | return class | |
| 357 | + | default: | |
| 358 | + | fStart := base | |
| 359 | + | fEnd := base + t.Size() | |
| 360 | + | if fStart >= end || fEnd <= start { | |
| 361 | + | return _NO_CLASS | |
| 362 | + | } | |
| 363 | + | switch t.Kind() { | |
| 364 | + | case reflect.Float32, reflect.Float64: | |
| 365 | + | return _SSE | |
| 366 | + | default: | |
| 367 | + | return _INTEGER | |
| 368 | + | } | |
| 369 | + | } | |
| 370 | + | } | |
| @@ -284,3 +284,87 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||||
| 284 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 284 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 285 | panic("purego: bundleStackArgs should not be called on amd64") | 285 | panic("purego: bundleStackArgs should not be called on amd64") | ||
| 286 | } | 286 | } | ||
| 287 | + | ||||
| 288 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 289 | + | outSize := ret.Type().Size() | |||
| 290 | + | switch { | |||
| 291 | + | case outSize == 0: | |||
| 292 | + | return | |||
| 293 | + | case outSize <= 16: | |||
| 294 | + | // Copy the struct's raw bytes (including padding) into a buffer. | |||
| 295 | + | var buf [2]uintptr | |||
| 296 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&buf[0])).Elem().Set(ret) | |||
| 297 | + | // Classify each eightbyte by the SysV ABI rules (§3.2.3, rule 4d | |||
| 298 | + | // of https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf). | |||
| 299 | + | // INTEGER wins over SSE for mixed eightbytes. Place INTEGER eightbytes in | |||
| 300 | + | // result[0]/result[1] (AX/DX) and SSE eightbytes in result[2]/result[3] | |||
| 301 | + | // (XMM0/XMM1). | |||
| 302 | + | // Assign each eightbyte to the next available register of the | |||
| 303 | + | // appropriate class. The ABI counts integer (AX, DX) and SSE | |||
| 304 | + | // (XMM0, XMM1) return registers independently. | |||
| 305 | + | var numInts int | |||
| 306 | + | var numFloats int | |||
| 307 | + | for i := 0; i < 2 && uintptr(i)*8 < outSize; i++ { | |||
| 308 | + | class := classifyEightbyte(ret.Type(), uintptr(i)*8, uintptr(i)*8+8) | |||
| 309 | + | if class == _SSE { | |||
| 310 | + | switch numFloats { | |||
| 311 | + | case 0: | |||
| 312 | + | a.result[2] = buf[i] | |||
| 313 | + | case 1: | |||
| 314 | + | a.result[3] = buf[i] | |||
| 315 | + | } | |||
| 316 | + | numFloats++ | |||
| 317 | + | } else { | |||
| 318 | + | switch numInts { | |||
| 319 | + | case 0: | |||
| 320 | + | a.result[0] = buf[i] | |||
| 321 | + | case 1: | |||
| 322 | + | a.result[1] = buf[i] | |||
| 323 | + | } | |||
| 324 | + | numInts++ | |||
| 325 | + | } | |||
| 326 | + | } | |||
| 327 | + | default: | |||
| 328 | + | // Structs > 16 bytes are returned by hidden pointer. | |||
| 329 | + | // a.result[0] contains the pointer passed by the caller in RDI. | |||
| 330 | + | // Write the struct through this pointer. | |||
| 331 | + | reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret) | |||
| 332 | + | } | |||
| 333 | + | } | |||
| 334 | + | ||||
| 335 | + | // classifyEightbyte returns the SysV ABI class for the byte range [start, end) | |||
| 336 | + | // within a type, by examining all scalar fields that overlap that range. | |||
| 337 | + | func classifyEightbyte(t reflect.Type, start, end uintptr) int { | |||
| 338 | + | return doClassifyEightbyte(t, 0, start, end) | |||
| 339 | + | } | |||
| 340 | + | ||||
| 341 | + | func doClassifyEightbyte(t reflect.Type, base, start, end uintptr) int { | |||
| 342 | + | switch t.Kind() { | |||
| 343 | + | case reflect.Struct: | |||
| 344 | + | class := _NO_CLASS | |||
| 345 | + | for i := 0; i < t.NumField(); i++ { | |||
| 346 | + | f := t.Field(i) | |||
| 347 | + | class |= doClassifyEightbyte(f.Type, base+f.Offset, start, end) | |||
| 348 | + | } | |||
| 349 | + | return class | |||
| 350 | + | case reflect.Array: | |||
| 351 | + | class := _NO_CLASS | |||
| 352 | + | elemSize := t.Elem().Size() | |||
| 353 | + | for i := 0; i < t.Len(); i++ { | |||
| 354 | + | class |= doClassifyEightbyte(t.Elem(), base+uintptr(i)*elemSize, start, end) | |||
| 355 | + | } | |||
| 356 | + | return class | |||
| 357 | + | default: | |||
| 358 | + | fStart := base | |||
| 359 | + | fEnd := base + t.Size() | |||
| 360 | + | if fStart >= end || fEnd <= start { | |||
| 361 | + | return _NO_CLASS | |||
| 362 | + | } | |||
| 363 | + | switch t.Kind() { | |||
| 364 | + | case reflect.Float32, reflect.Float64: | |||
| 365 | + | return _SSE | |||
| 366 | + | default: | |||
| 367 | + | return _INTEGER | |||
| 368 | + | } | |||
| 369 | + | } | |||
| 370 | + | } | |||
M struct_arm.go +4 -0
| @@ -83,3 +83,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||
| 83 | 83 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 84 | 84 | panic("purego: bundleStackArgs should not be called on arm") | |
| 85 | 85 | } | |
| 86 | + | ||
| 87 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 88 | + | panic("purego: struct returns are not supported on arm") | |
| 89 | + | } | |
| @@ -83,3 +83,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||||
| 83 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 83 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 84 | panic("purego: bundleStackArgs should not be called on arm") | 84 | panic("purego: bundleStackArgs should not be called on arm") | ||
| 85 | } | 85 | } | ||
| 86 | + | ||||
| 87 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 88 | + | panic("purego: struct returns are not supported on arm") | |||
| 89 | + | } | |||
M struct_arm64.go +50 -6
| @@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 24 | 24 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | |
| 25 | 25 | r1 = syscall.f1 | |
| 26 | 26 | if numFields == 2 { | |
| 27 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 27 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 | |
| 28 | 28 | } | |
| 29 | 29 | } | |
| 30 | 30 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| @@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 33 | 33 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | |
| 34 | 34 | switch numFields { | |
| 35 | 35 | case 4: | |
| 36 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 37 | - | r2 = syscall.f4<<32 | syscall.f3 | |
| 36 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 | |
| 37 | + | r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32 | |
| 38 | 38 | case 3: | |
| 39 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 39 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 | |
| 40 | 40 | r2 = syscall.f3 | |
| 41 | 41 | case 2: | |
| 42 | 42 | r1 = syscall.f1 | |
| 43 | 43 | r2 = syscall.f2 | |
| 44 | 44 | default: | |
| 45 | - | panic("unreachable") | |
| 45 | + | panic("not reached") | |
| 46 | 46 | } | |
| 47 | 47 | } | |
| 48 | 48 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| @@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 54 | 54 | case 3: | |
| 55 | 55 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem() | |
| 56 | 56 | default: | |
| 57 | - | panic("unreachable") | |
| 57 | + | panic("not reached") | |
| 58 | 58 | } | |
| 59 | 59 | } | |
| 60 | 60 | // create struct from the Go pointer created in arm64_r8 | |
| @@ -547,3 +547,47 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 547 | 547 | size := structType.Size() | |
| 548 | 548 | copyStruct8ByteChunks(ptr, size, addStack) | |
| 549 | 549 | } | |
| 550 | + | ||
| 551 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 552 | + | outSize := ret.Type().Size() | |
| 553 | + | switch { | |
| 554 | + | case outSize == 0: | |
| 555 | + | return | |
| 556 | + | case outSize <= 8: | |
| 557 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |
| 558 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 { | |
| 559 | + | a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1] | |
| 560 | + | a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument | |
| 561 | + | } | |
| 562 | + | return | |
| 563 | + | case outSize <= 16: | |
| 564 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |
| 565 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats { | |
| 566 | + | switch numFields { | |
| 567 | + | case 4: | |
| 568 | + | a.result[3] = a.result[1] >> 32 | |
| 569 | + | a.result[2] = a.result[1] & math.MaxUint32 | |
| 570 | + | a.result[1] = a.result[0] >> 32 | |
| 571 | + | a.result[0] &= math.MaxUint32 | |
| 572 | + | case 3: | |
| 573 | + | a.result[2] = a.result[1] & math.MaxUint32 | |
| 574 | + | a.result[1] = a.result[0] >> 32 | |
| 575 | + | a.result[0] &= math.MaxUint32 | |
| 576 | + | case 2: | |
| 577 | + | // two float64s are already in a.result[0] and a.result[1] | |
| 578 | + | default: | |
| 579 | + | panic("not reached") | |
| 580 | + | } | |
| 581 | + | } | |
| 582 | + | return | |
| 583 | + | default: | |
| 584 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 { | |
| 585 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |
| 586 | + | return | |
| 587 | + | } | |
| 588 | + | // The caller passed the address to place the return struct, | |
| 589 | + | // so copy the Go struct into the provided memory. | |
| 590 | + | reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret) | |
| 591 | + | return | |
| 592 | + | } | |
| 593 | + | } | |
| @@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 24 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | 24 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | ||
| 25 | r1 = syscall.f1 | 25 | r1 = syscall.f1 | ||
| 26 | if numFields == 2 { | 26 | if numFields == 2 { | ||
| 27 | - | r1 = syscall.f2<<32 | syscall.f1 | 27 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 |
| 28 | } | 28 | } | ||
| 29 | } | 29 | } | ||
| 30 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | 30 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | ||
| @@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 33 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | 33 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | ||
| 34 | switch numFields { | 34 | switch numFields { | ||
| 35 | case 4: | 35 | case 4: | ||
| 36 | - | r1 = syscall.f2<<32 | syscall.f1 | 36 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 |
| 37 | - | r2 = syscall.f4<<32 | syscall.f3 | 37 | + | r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32 |
| 38 | case 3: | 38 | case 3: | ||
| 39 | - | r1 = syscall.f2<<32 | syscall.f1 | 39 | + | r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32 |
| 40 | r2 = syscall.f3 | 40 | r2 = syscall.f3 | ||
| 41 | case 2: | 41 | case 2: | ||
| 42 | r1 = syscall.f1 | 42 | r1 = syscall.f1 | ||
| 43 | r2 = syscall.f2 | 43 | r2 = syscall.f2 | ||
| 44 | default: | 44 | default: | ||
| 45 | - | panic("unreachable") | 45 | + | panic("not reached") |
| 46 | } | 46 | } | ||
| 47 | } | 47 | } | ||
| 48 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | 48 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | ||
| @@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 54 | case 3: | 54 | case 3: | ||
| 55 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem() | 55 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem() | ||
| 56 | default: | 56 | default: | ||
| 57 | - | panic("unreachable") | 57 | + | panic("not reached") |
| 58 | } | 58 | } | ||
| 59 | } | 59 | } | ||
| 60 | // create struct from the Go pointer created in arm64_r8 | 60 | // create struct from the Go pointer created in arm64_r8 | ||
| @@ -547,3 +547,47 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 547 | size := structType.Size() | 547 | size := structType.Size() | ||
| 548 | copyStruct8ByteChunks(ptr, size, addStack) | 548 | copyStruct8ByteChunks(ptr, size, addStack) | ||
| 549 | } | 549 | } | ||
| 550 | + | ||||
| 551 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 552 | + | outSize := ret.Type().Size() | |||
| 553 | + | switch { | |||
| 554 | + | case outSize == 0: | |||
| 555 | + | return | |||
| 556 | + | case outSize <= 8: | |||
| 557 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |||
| 558 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 { | |||
| 559 | + | a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1] | |||
| 560 | + | a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument | |||
| 561 | + | } | |||
| 562 | + | return | |||
| 563 | + | case outSize <= 16: | |||
| 564 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |||
| 565 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats { | |||
| 566 | + | switch numFields { | |||
| 567 | + | case 4: | |||
| 568 | + | a.result[3] = a.result[1] >> 32 | |||
| 569 | + | a.result[2] = a.result[1] & math.MaxUint32 | |||
| 570 | + | a.result[1] = a.result[0] >> 32 | |||
| 571 | + | a.result[0] &= math.MaxUint32 | |||
| 572 | + | case 3: | |||
| 573 | + | a.result[2] = a.result[1] & math.MaxUint32 | |||
| 574 | + | a.result[1] = a.result[0] >> 32 | |||
| 575 | + | a.result[0] &= math.MaxUint32 | |||
| 576 | + | case 2: | |||
| 577 | + | // two float64s are already in a.result[0] and a.result[1] | |||
| 578 | + | default: | |||
| 579 | + | panic("not reached") | |||
| 580 | + | } | |||
| 581 | + | } | |||
| 582 | + | return | |||
| 583 | + | default: | |||
| 584 | + | if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 { | |||
| 585 | + | reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret) | |||
| 586 | + | return | |||
| 587 | + | } | |||
| 588 | + | // The caller passed the address to place the return struct, | |||
| 589 | + | // so copy the Go struct into the provided memory. | |||
| 590 | + | reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret) | |||
| 591 | + | return | |||
| 592 | + | } | |||
| 593 | + | } | |||
M struct_loong64.go +5 -1
| @@ -37,7 +37,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 37 | 37 | r1 = syscall.f1 | |
| 38 | 38 | r2 = syscall.f2 | |
| 39 | 39 | default: | |
| 40 | - | panic("unreachable") | |
| 40 | + | panic("not reached") | |
| 41 | 41 | } | |
| 42 | 42 | } | |
| 43 | 43 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| @@ -211,3 +211,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||
| 211 | 211 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 212 | 212 | panic("purego: bundleStackArgs should not be called on loong64") | |
| 213 | 213 | } | |
| 214 | + | ||
| 215 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 216 | + | panic("purego: struct returns are not supported on loong64") | |
| 217 | + | } | |
| @@ -37,7 +37,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 37 | r1 = syscall.f1 | 37 | r1 = syscall.f1 | ||
| 38 | r2 = syscall.f2 | 38 | r2 = syscall.f2 | ||
| 39 | default: | 39 | default: | ||
| 40 | - | panic("unreachable") | 40 | + | panic("not reached") |
| 41 | } | 41 | } | ||
| 42 | } | 42 | } | ||
| 43 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | 43 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | ||
| @@ -211,3 +211,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int | |||||
| 211 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 211 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 212 | panic("purego: bundleStackArgs should not be called on loong64") | 212 | panic("purego: bundleStackArgs should not be called on loong64") | ||
| 213 | } | 213 | } | ||
| 214 | + | ||||
| 215 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 216 | + | panic("purego: struct returns are not supported on loong64") | |||
| 217 | + | } | |||
M struct_ppc64le.go +5 -1
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||
| 139 | 139 | } | |
| 140 | 140 | ||
| 141 | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 142 | - | panic("bundleStackArgs not supported on PPC64LE") | |
| 142 | + | panic("purego: bundleStackArgs should not be called on ppc64le") | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | + | panic("purego: struct returns are not supported on ppc64le") | |
| 143 | 147 | } | |
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||||
| 139 | } | 139 | } | ||
| 140 | 140 | ||||
| 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 142 | - | panic("bundleStackArgs not supported on PPC64LE") | 142 | + | panic("purego: bundleStackArgs should not be called on ppc64le") |
| 143 | + | } | |||
| 144 | + | ||||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 146 | + | panic("purego: struct returns are not supported on ppc64le") | |||
| 143 | } | 147 | } | ||
M struct_riscv64.go +5 -1
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||
| 139 | 139 | } | |
| 140 | 140 | ||
| 141 | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 142 | - | panic("bundleStackArgs not supported on RISCV64") | |
| 142 | + | panic("purego: bundleStackArgs should not be called on riscv64") | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | + | panic("purego: struct returns are not supported on riscv64") | |
| 143 | 147 | } | |
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||||
| 139 | } | 139 | } | ||
| 140 | 140 | ||||
| 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 142 | - | panic("bundleStackArgs not supported on RISCV64") | 142 | + | panic("purego: bundleStackArgs should not be called on riscv64") |
| 143 | + | } | |||
| 144 | + | ||||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 146 | + | panic("purego: struct returns are not supported on riscv64") | |||
| 143 | } | 147 | } | ||
M struct_s390x.go +5 -1
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||
| 139 | 139 | } | |
| 140 | 140 | ||
| 141 | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |
| 142 | - | panic("bundleStackArgs not supported on S390X") | |
| 142 | + | panic("purego: bundleStackArgs should not be called on s390x") | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | + | panic("purego: struct returns are not supported on s390x") | |
| 143 | 147 | } | |
| @@ -139,5 +139,9 @@ func collectStackArgs( | |||||
| 139 | } | 139 | } | ||
| 140 | 140 | ||||
| 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | 141 | func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | ||
| 142 | - | panic("bundleStackArgs not supported on S390X") | 142 | + | panic("purego: bundleStackArgs should not be called on s390x") |
| 143 | + | } | |||
| 144 | + | ||||
| 145 | + | func setStruct(a *callbackArgs, ret reflect.Value) { | |||
| 146 | + | panic("purego: struct returns are not supported on s390x") | |||
| 143 | } | 147 | } | ||
M struct_test.go +395 -315
| @@ -9,6 +9,7 @@ import ( | |||
| 9 | 9 | "math" | |
| 10 | 10 | "os" | |
| 11 | 11 | "path/filepath" | |
| 12 | + | "reflect" | |
| 12 | 13 | "runtime" | |
| 13 | 14 | "testing" | |
| 14 | 15 | "unsafe" | |
| @@ -532,6 +533,41 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||
| 532 | 533 | } | |
| 533 | 534 | } | |
| 534 | 535 | ||
| 536 | + | // TODO: this could use the iter.Seq interface when purego supports Go 1.23 | |
| 537 | + | func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) { | |
| 538 | + | var fieldIndex int | |
| 539 | + | var tracker func() (reflect.Value, bool) | |
| 540 | + | return func() (reflect.Value, bool) { | |
| 541 | + | if v.NumField() == 0 { | |
| 542 | + | return reflect.Value{}, false | |
| 543 | + | } | |
| 544 | + | if tracker != nil { | |
| 545 | + | if field, ok := tracker(); ok { | |
| 546 | + | return field, ok | |
| 547 | + | } | |
| 548 | + | tracker = nil | |
| 549 | + | } | |
| 550 | + | for fieldIndex < v.NumField() { | |
| 551 | + | if v.Type().Field(fieldIndex).Name == "_" { | |
| 552 | + | fieldIndex++ | |
| 553 | + | continue | |
| 554 | + | } | |
| 555 | + | field := v.Field(fieldIndex) | |
| 556 | + | fieldIndex++ | |
| 557 | + | if field.Kind() == reflect.Struct { | |
| 558 | + | tracker = nextFieldFn(field) | |
| 559 | + | if inner, ok := tracker(); ok { | |
| 560 | + | return inner, ok | |
| 561 | + | } | |
| 562 | + | tracker = nil | |
| 563 | + | } else { | |
| 564 | + | return field, true | |
| 565 | + | } | |
| 566 | + | } | |
| 567 | + | return reflect.Value{}, false | |
| 568 | + | } | |
| 569 | + | } | |
| 570 | + | ||
| 535 | 571 | func TestRegisterFunc_structReturns(t *testing.T) { | |
| 536 | 572 | libFileName := filepath.Join(t.TempDir(), "structreturntest.so") | |
| 537 | 573 | t.Logf("Build %v", libFileName) | |
| @@ -545,321 +581,365 @@ func TestRegisterFunc_structReturns(t *testing.T) { | |||
| 545 | 581 | if err != nil { | |
| 546 | 582 | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |
| 547 | 583 | } | |
| 584 | + | implementations := []struct { | |
| 585 | + | name string | |
| 586 | + | register func(fptr any, handle uintptr, name string) | |
| 587 | + | }{ | |
| 588 | + | { | |
| 589 | + | name: "RegisterLibFunc", | |
| 590 | + | register: purego.RegisterLibFunc, | |
| 591 | + | }, | |
| 592 | + | { | |
| 593 | + | name: "GoCallbackFunc", | |
| 594 | + | register: func(fptr any, _ uintptr, _ string) { | |
| 595 | + | fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value { | |
| 596 | + | retType := reflect.TypeOf(fptr).Elem().Out(0) | |
| 597 | + | ret := reflect.New(retType).Elem() | |
| 598 | + | next := nextFieldFn(ret) | |
| 599 | + | for _, a := range args { | |
| 600 | + | field, ok := next() | |
| 601 | + | if !ok { | |
| 602 | + | panic("purego: no more fields") | |
| 603 | + | } | |
| 604 | + | field.Set(a) | |
| 605 | + | } | |
| 606 | + | return []reflect.Value{ret} | |
| 607 | + | }) | |
| 608 | + | purego.RegisterFunc(fptr, purego.NewCallback(fn.Interface())) | |
| 609 | + | }, | |
| 610 | + | }, | |
| 611 | + | } | |
| 612 | + | for _, imp := range implementations { | |
| 613 | + | imp := imp | |
| 614 | + | t.Run(imp.name, func(t *testing.T) { | |
| 615 | + | register := imp.register | |
| 616 | + | { | |
| 617 | + | type Empty struct{} | |
| 618 | + | var ReturnEmpty func() Empty | |
| 619 | + | register(&ReturnEmpty, lib, "ReturnEmpty") | |
| 620 | + | ret := ReturnEmpty() | |
| 621 | + | _ = ret | |
| 622 | + | } | |
| 623 | + | { | |
| 624 | + | type inner struct{ A int16 } | |
| 625 | + | type StructInStruct struct { | |
| 626 | + | A inner | |
| 627 | + | B inner | |
| 628 | + | C inner | |
| 629 | + | } | |
| 630 | + | var ReturnStructInStruct func(a, b, c int16) StructInStruct | |
| 631 | + | register(&ReturnStructInStruct, lib, "ReturnStructInStruct") | |
| 632 | + | expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}} | |
| 633 | + | if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected { | |
| 634 | + | t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected) | |
| 635 | + | } | |
| 636 | + | } | |
| 637 | + | { | |
| 638 | + | type ThreeShorts struct{ A, B, C int16 } | |
| 639 | + | var ReturnThreeShorts func(a, b, c int16) ThreeShorts | |
| 640 | + | register(&ReturnThreeShorts, lib, "ReturnThreeShorts") | |
| 641 | + | expected := ThreeShorts{^int16(0), 2, 3} | |
| 642 | + | if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected { | |
| 643 | + | t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected) | |
| 644 | + | } | |
| 645 | + | } | |
| 646 | + | { | |
| 647 | + | type FourShorts struct{ A, B, C, D int16 } | |
| 648 | + | var ReturnFourShorts func(a, b, c, d int16) FourShorts | |
| 649 | + | register(&ReturnFourShorts, lib, "ReturnFourShorts") | |
| 650 | + | expected := FourShorts{^int16(0), 2, 3, 4} | |
| 651 | + | if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected { | |
| 652 | + | t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected) | |
| 653 | + | } | |
| 654 | + | } | |
| 655 | + | { | |
| 656 | + | type OneLong struct{ A int64 } | |
| 657 | + | var ReturnOneLong func(a int64) OneLong | |
| 658 | + | register(&ReturnOneLong, lib, "ReturnOneLong") | |
| 659 | + | expected := OneLong{5} | |
| 660 | + | if ret := ReturnOneLong(5); ret != expected { | |
| 661 | + | t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected) | |
| 662 | + | } | |
| 663 | + | } | |
| 664 | + | { | |
| 665 | + | type TwoLongs struct{ A, B int64 } | |
| 666 | + | var ReturnTwoLongs func(a, b int64) TwoLongs | |
| 667 | + | register(&ReturnTwoLongs, lib, "ReturnTwoLongs") | |
| 668 | + | expected := TwoLongs{1, 2} | |
| 669 | + | if ret := ReturnTwoLongs(1, 2); ret != expected { | |
| 670 | + | t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected) | |
| 671 | + | } | |
| 672 | + | } | |
| 673 | + | { | |
| 674 | + | type ThreeLongs struct{ A, B, C int64 } | |
| 675 | + | var ReturnThreeLongs func(a, b, c int64) ThreeLongs | |
| 676 | + | register(&ReturnThreeLongs, lib, "ReturnThreeLongs") | |
| 677 | + | expected := ThreeLongs{1, 2, 3} | |
| 678 | + | if ret := ReturnThreeLongs(1, 2, 3); ret != expected { | |
| 679 | + | t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected) | |
| 680 | + | } | |
| 681 | + | } | |
| 682 | + | { | |
| 683 | + | type OneFloat struct{ A float32 } | |
| 684 | + | var ReturnOneFloat func(a float32) OneFloat | |
| 685 | + | register(&ReturnOneFloat, lib, "ReturnOneFloat") | |
| 686 | + | expected := OneFloat{1} | |
| 687 | + | if ret := ReturnOneFloat(1); ret != expected { | |
| 688 | + | t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected) | |
| 689 | + | } | |
| 690 | + | } | |
| 691 | + | { | |
| 692 | + | type TwoFloats struct{ A, B float32 } | |
| 693 | + | var ReturnTwoFloats func(a, b float32) TwoFloats | |
| 694 | + | register(&ReturnTwoFloats, lib, "ReturnTwoFloats") | |
| 695 | + | expected := TwoFloats{5, 2} | |
| 696 | + | if ret := ReturnTwoFloats(5, 2); ret != expected { | |
| 697 | + | t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected) | |
| 698 | + | } | |
| 699 | + | } | |
| 700 | + | { | |
| 701 | + | type ThreeFloats struct{ A, B, C float32 } | |
| 702 | + | var ReturnThreeFloats func(a, b, c float32) ThreeFloats | |
| 703 | + | register(&ReturnThreeFloats, lib, "ReturnThreeFloats") | |
| 704 | + | expected := ThreeFloats{1, 2, 3} | |
| 705 | + | if ret := ReturnThreeFloats(1, 2, 3); ret != expected { | |
| 706 | + | t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected) | |
| 707 | + | } | |
| 708 | + | } | |
| 709 | + | { | |
| 710 | + | type FourFloats struct{ A, B, C, D float32 } | |
| 711 | + | var ReturnFourFloats func(a, b, c, d float32) FourFloats | |
| 712 | + | register(&ReturnFourFloats, lib, "ReturnFourFloats") | |
| 713 | + | expected := FourFloats{1, 2, 3, 4} | |
| 714 | + | if ret := ReturnFourFloats(1, 2, 3, 4); ret != expected { | |
| 715 | + | t.Fatalf("ReturnFourFloats returned %+v wanted %+v", ret, expected) | |
| 716 | + | } | |
| 717 | + | } | |
| 718 | + | { | |
| 719 | + | type OneDouble struct{ A float64 } | |
| 720 | + | var ReturnOneDouble func(a float64) OneDouble | |
| 721 | + | register(&ReturnOneDouble, lib, "ReturnOneDouble") | |
| 722 | + | expected := OneDouble{1} | |
| 723 | + | if ret := ReturnOneDouble(1); ret != expected { | |
| 724 | + | t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected) | |
| 725 | + | } | |
| 726 | + | } | |
| 727 | + | { | |
| 728 | + | type TwoDoubles struct{ A, B float64 } | |
| 729 | + | var ReturnTwoDoubles func(a, b float64) TwoDoubles | |
| 730 | + | register(&ReturnTwoDoubles, lib, "ReturnTwoDoubles") | |
| 731 | + | expected := TwoDoubles{7, 3} | |
| 732 | + | if ret := ReturnTwoDoubles(7, 3); ret != expected { | |
| 733 | + | t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected) | |
| 734 | + | } | |
| 735 | + | } | |
| 736 | + | { | |
| 737 | + | type ThreeDoubles struct{ A, B, C float64 } | |
| 738 | + | var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles | |
| 739 | + | register(&ReturnThreeDoubles, lib, "ReturnThreeDoubles") | |
| 740 | + | expected := ThreeDoubles{1, 2, 3} | |
| 741 | + | if ret := ReturnThreeDoubles(1, 2, 3); ret != expected { | |
| 742 | + | t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected) | |
| 743 | + | } | |
| 744 | + | } | |
| 745 | + | { | |
| 746 | + | type FourDoubles struct{ A, B, C, D float64 } | |
| 747 | + | var ReturnFourDoubles func(a, b, c, d float64) FourDoubles | |
| 748 | + | register(&ReturnFourDoubles, lib, "ReturnFourDoubles") | |
| 749 | + | expected := FourDoubles{1, 2, 3, 4} | |
| 750 | + | if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected { | |
| 751 | + | t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected) | |
| 752 | + | } | |
| 753 | + | } | |
| 754 | + | { | |
| 755 | + | type FourDoublesInternal struct { | |
| 756 | + | F struct{ A, B float64 } | |
| 757 | + | G struct{ C, D float64 } | |
| 758 | + | } | |
| 759 | + | var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal | |
| 760 | + | register(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal") | |
| 761 | + | expected := FourDoublesInternal{F: struct{ A, B float64 }{A: 1, B: 2}, G: struct{ C, D float64 }{C: 3, D: 4}} | |
| 762 | + | if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected { | |
| 763 | + | t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected) | |
| 764 | + | } | |
| 765 | + | } | |
| 766 | + | { | |
| 767 | + | type FiveDoubles struct{ A, B, C, D, E float64 } | |
| 768 | + | var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles | |
| 769 | + | register(&ReturnFiveDoubles, lib, "ReturnFiveDoubles") | |
| 770 | + | expected := FiveDoubles{1, 2, 3, 4, 5} | |
| 771 | + | if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected { | |
| 772 | + | t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected) | |
| 773 | + | } | |
| 774 | + | } | |
| 775 | + | { | |
| 776 | + | type OneFloatOneDouble struct { | |
| 777 | + | A float32 | |
| 778 | + | _ float32 | |
| 779 | + | B float64 | |
| 780 | + | } | |
| 781 | + | var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble | |
| 782 | + | register(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble") | |
| 783 | + | expected := OneFloatOneDouble{A: 1, B: 2} | |
| 784 | + | if ret := ReturnOneFloatOneDouble(1, 2); ret != expected { | |
| 785 | + | t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected) | |
| 786 | + | } | |
| 787 | + | } | |
| 788 | + | { | |
| 789 | + | type OneDoubleOneFloat struct { | |
| 790 | + | A float64 | |
| 791 | + | B float32 | |
| 792 | + | } | |
| 793 | + | var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat | |
| 794 | + | register(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat") | |
| 795 | + | expected := OneDoubleOneFloat{1, 2} | |
| 796 | + | if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected { | |
| 797 | + | t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected) | |
| 798 | + | } | |
| 799 | + | } | |
| 800 | + | { | |
| 801 | + | type Unaligned1 struct { | |
| 802 | + | A int8 | |
| 803 | + | _ [1]int8 | |
| 804 | + | B int16 | |
| 805 | + | _ [1]int32 | |
| 806 | + | C int64 | |
| 807 | + | } | |
| 808 | + | var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1 | |
| 809 | + | register(&ReturnUnaligned1, lib, "ReturnUnaligned1") | |
| 810 | + | expected := Unaligned1{A: 1, B: 2, C: 3} | |
| 811 | + | if ret := ReturnUnaligned1(1, 2, 3); ret != expected { | |
| 812 | + | t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected) | |
| 813 | + | } | |
| 814 | + | } | |
| 548 | 815 | ||
| 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) | |
| 816 | + | { | |
| 817 | + | type Mixed1 struct { | |
| 818 | + | A float32 | |
| 819 | + | B int32 | |
| 820 | + | } | |
| 821 | + | var ReturnMixed1 func(a float32, b int32) Mixed1 | |
| 822 | + | register(&ReturnMixed1, lib, "ReturnMixed1") | |
| 823 | + | expected := Mixed1{1, 2} | |
| 824 | + | if ret := ReturnMixed1(1, 2); ret != expected { | |
| 825 | + | t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected) | |
| 826 | + | } | |
| 827 | + | } | |
| 828 | + | { | |
| 829 | + | type Mixed2 struct { | |
| 830 | + | A float32 | |
| 831 | + | B int32 | |
| 832 | + | C float32 | |
| 833 | + | D int32 | |
| 834 | + | } | |
| 835 | + | var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2 | |
| 836 | + | register(&ReturnMixed2, lib, "ReturnMixed2") | |
| 837 | + | expected := Mixed2{1, 2, 3, 4} | |
| 838 | + | if ret := ReturnMixed2(1, 2, 3, 4); ret != expected { | |
| 839 | + | t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected) | |
| 840 | + | } | |
| 841 | + | } | |
| 842 | + | { | |
| 843 | + | type Mixed3 struct { | |
| 844 | + | A float32 | |
| 845 | + | B uint32 | |
| 846 | + | C float64 | |
| 847 | + | } | |
| 848 | + | var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3 | |
| 849 | + | register(&ReturnMixed3, lib, "ReturnMixed3") | |
| 850 | + | expected := Mixed3{1, 2, 3} | |
| 851 | + | if ret := ReturnMixed3(1, 2, 3); ret != expected { | |
| 852 | + | t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected) | |
| 853 | + | } | |
| 854 | + | } | |
| 855 | + | { | |
| 856 | + | type Mixed4 struct { | |
| 857 | + | A float64 | |
| 858 | + | B uint32 | |
| 859 | + | C float32 | |
| 860 | + | } | |
| 861 | + | var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4 | |
| 862 | + | register(&ReturnMixed4, lib, "ReturnMixed4") | |
| 863 | + | expected := Mixed4{1, 2, 3} | |
| 864 | + | if ret := ReturnMixed4(1, 2, 3); ret != expected { | |
| 865 | + | t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected) | |
| 866 | + | } | |
| 867 | + | } | |
| 868 | + | { | |
| 869 | + | type Mixed5 struct { | |
| 870 | + | A *int64 | |
| 871 | + | B int32 | |
| 872 | + | C float32 | |
| 873 | + | D int32 | |
| 874 | + | } | |
| 875 | + | var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5 | |
| 876 | + | register(&ReturnMixed5, lib, "ReturnMixed5") | |
| 877 | + | ptr := new(int64) | |
| 878 | + | expected := Mixed5{ptr, 1, 7.2, 9} | |
| 879 | + | if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected { | |
| 880 | + | t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected) | |
| 881 | + | } | |
| 882 | + | runtime.KeepAlive(ptr) | |
| 883 | + | } | |
| 884 | + | { | |
| 885 | + | type Mixed5 struct { | |
| 886 | + | A *int64 | |
| 887 | + | B int32 | |
| 888 | + | C float32 | |
| 889 | + | D int32 | |
| 890 | + | } | |
| 891 | + | var IdentityMixed5 func(m Mixed5) Mixed5 | |
| 892 | + | // TODO: when struct args are supported in NewCallback use register() instead | |
| 893 | + | purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5") | |
| 894 | + | ptr := new(int64) | |
| 895 | + | expected := Mixed5{ptr, 1, 7.2, 9} | |
| 896 | + | if ret := IdentityMixed5(expected); ret != expected { | |
| 897 | + | t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected) | |
| 898 | + | } | |
| 899 | + | runtime.KeepAlive(ptr) | |
| 900 | + | } | |
| 901 | + | { | |
| 902 | + | type SmallBool struct { | |
| 903 | + | A bool | |
| 904 | + | B int32 | |
| 905 | + | C int64 | |
| 906 | + | } | |
| 907 | + | var ReturnSmallBool func(a bool, b int32, c int64) SmallBool | |
| 908 | + | register(&ReturnSmallBool, lib, "ReturnSmallBool") | |
| 909 | + | expected := SmallBool{true, 42, 123456789} | |
| 910 | + | if ret := ReturnSmallBool(true, 42, 123456789); ret != expected { | |
| 911 | + | t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected) | |
| 912 | + | } | |
| 913 | + | } | |
| 914 | + | { | |
| 915 | + | type LargeBool struct { | |
| 916 | + | A bool | |
| 917 | + | B int32 | |
| 918 | + | C int64 | |
| 919 | + | D int64 | |
| 920 | + | } | |
| 921 | + | var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool | |
| 922 | + | register(&ReturnLargeBool, lib, "ReturnLargeBool") | |
| 923 | + | expected := LargeBool{false, -99, 987654321, 111222333444} | |
| 924 | + | if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected { | |
| 925 | + | t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected) | |
| 926 | + | } | |
| 927 | + | } | |
| 928 | + | { | |
| 929 | + | type Ptr1 struct { | |
| 930 | + | a *int64 | |
| 931 | + | b unsafe.Pointer | |
| 932 | + | } | |
| 933 | + | var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1 | |
| 934 | + | purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1") | |
| 935 | + | a, b := new(int64), new(struct{}) | |
| 936 | + | expected := Ptr1{a, unsafe.Pointer(b)} | |
| 937 | + | if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected { | |
| 938 | + | t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected) | |
| 939 | + | } | |
| 940 | + | runtime.KeepAlive(a) | |
| 941 | + | runtime.KeepAlive(b) | |
| 942 | + | } | |
| 943 | + | }) | |
| 864 | 944 | } | |
| 865 | 945 | } | |
| @@ -9,6 +9,7 @@ import ( | |||||
| 9 | "math" | 9 | "math" | ||
| 10 | "os" | 10 | "os" | ||
| 11 | "path/filepath" | 11 | "path/filepath" | ||
| 12 | + | "reflect" | |||
| 12 | "runtime" | 13 | "runtime" | ||
| 13 | "testing" | 14 | "testing" | ||
| 14 | "unsafe" | 15 | "unsafe" | ||
| @@ -532,6 +533,41 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||||
| 532 | } | 533 | } | ||
| 533 | } | 534 | } | ||
| 534 | 535 | ||||
| 536 | + | // TODO: this could use the iter.Seq interface when purego supports Go 1.23 | |||
| 537 | + | func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) { | |||
| 538 | + | var fieldIndex int | |||
| 539 | + | var tracker func() (reflect.Value, bool) | |||
| 540 | + | return func() (reflect.Value, bool) { | |||
| 541 | + | if v.NumField() == 0 { | |||
| 542 | + | return reflect.Value{}, false | |||
| 543 | + | } | |||
| 544 | + | if tracker != nil { | |||
| 545 | + | if field, ok := tracker(); ok { | |||
| 546 | + | return field, ok | |||
| 547 | + | } | |||
| 548 | + | tracker = nil | |||
| 549 | + | } | |||
| 550 | + | for fieldIndex < v.NumField() { | |||
| 551 | + | if v.Type().Field(fieldIndex).Name == "_" { | |||
| 552 | + | fieldIndex++ | |||
| 553 | + | continue | |||
| 554 | + | } | |||
| 555 | + | field := v.Field(fieldIndex) | |||
| 556 | + | fieldIndex++ | |||
| 557 | + | if field.Kind() == reflect.Struct { | |||
| 558 | + | tracker = nextFieldFn(field) | |||
| 559 | + | if inner, ok := tracker(); ok { | |||
| 560 | + | return inner, ok | |||
| 561 | + | } | |||
| 562 | + | tracker = nil | |||
| 563 | + | } else { | |||
| 564 | + | return field, true | |||
| 565 | + | } | |||
| 566 | + | } | |||
| 567 | + | return reflect.Value{}, false | |||
| 568 | + | } | |||
| 569 | + | } | |||
| 570 | + | ||||
| 535 | func TestRegisterFunc_structReturns(t *testing.T) { | 571 | func TestRegisterFunc_structReturns(t *testing.T) { | ||
| 536 | libFileName := filepath.Join(t.TempDir(), "structreturntest.so") | 572 | libFileName := filepath.Join(t.TempDir(), "structreturntest.so") | ||
| 537 | t.Logf("Build %v", libFileName) | 573 | t.Logf("Build %v", libFileName) | ||
| @@ -545,321 +581,365 @@ func TestRegisterFunc_structReturns(t *testing.T) { | |||||
| 545 | if err != nil { | 581 | if err != nil { | ||
| 546 | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | 582 | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | ||
| 547 | } | 583 | } | ||
| 584 | + | implementations := []struct { | |||
| 585 | + | name string | |||
| 586 | + | register func(fptr any, handle uintptr, name string) | |||
| 587 | + | }{ | |||
| 588 | + | { | |||
| 589 | + | name: "RegisterLibFunc", | |||
| 590 | + | register: purego.RegisterLibFunc, | |||
| 591 | + | }, | |||
| 592 | + | { | |||
| 593 | + | name: "GoCallbackFunc", | |||
| 594 | + | register: func(fptr any, _ uintptr, _ string) { | |||
| 595 | + | fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value { | |||
| 596 | + | retType := reflect.TypeOf(fptr).Elem().Out(0) | |||
| 597 | + | ret := reflect.New(retType).Elem() | |||
| 598 | + | next := nextFieldFn(ret) | |||
| 599 | + | for _, a := range args { | |||
| 600 | + | field, ok := next() | |||
| 601 | + | if !ok { | |||
| 602 | + | panic("purego: no more fields") | |||
| 603 | + | } | |||
| 604 | + | field.Set(a) | |||
| 605 | + | } | |||
| 606 | + | return []reflect.Value{ret} | |||
| 607 | + | }) | |||
| 608 | + | purego.RegisterFunc(fptr, purego.NewCallback(fn.Interface())) | |||
| 609 | + | }, | |||
| 610 | + | }, | |||
| 611 | + | } | |||
| 612 | + | for _, imp := range implementations { | |||
| 613 | + | imp := imp | |||
| 614 | + | t.Run(imp.name, func(t *testing.T) { | |||
| 615 | + | register := imp.register | |||
| 616 | + | { | |||
| 617 | + | type Empty struct{} | |||
| 618 | + | var ReturnEmpty func() Empty | |||
| 619 | + | register(&ReturnEmpty, lib, "ReturnEmpty") | |||
| 620 | + | ret := ReturnEmpty() | |||
| 621 | + | _ = ret | |||
| 622 | + | } | |||
| 623 | + | { | |||
| 624 | + | type inner struct{ A int16 } | |||
| 625 | + | type StructInStruct struct { | |||
| 626 | + | A inner | |||
| 627 | + | B inner | |||
| 628 | + | C inner | |||
| 629 | + | } | |||
| 630 | + | var ReturnStructInStruct func(a, b, c int16) StructInStruct | |||
| 631 | + | register(&ReturnStructInStruct, lib, "ReturnStructInStruct") | |||
| 632 | + | expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}} | |||
| 633 | + | if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected { | |||
| 634 | + | t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected) | |||
| 635 | + | } | |||
| 636 | + | } | |||
| 637 | + | { | |||
| 638 | + | type ThreeShorts struct{ A, B, C int16 } | |||
| 639 | + | var ReturnThreeShorts func(a, b, c int16) ThreeShorts | |||
| 640 | + | register(&ReturnThreeShorts, lib, "ReturnThreeShorts") | |||
| 641 | + | expected := ThreeShorts{^int16(0), 2, 3} | |||
| 642 | + | if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected { | |||
| 643 | + | t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected) | |||
| 644 | + | } | |||
| 645 | + | } | |||
| 646 | + | { | |||
| 647 | + | type FourShorts struct{ A, B, C, D int16 } | |||
| 648 | + | var ReturnFourShorts func(a, b, c, d int16) FourShorts | |||
| 649 | + | register(&ReturnFourShorts, lib, "ReturnFourShorts") | |||
| 650 | + | expected := FourShorts{^int16(0), 2, 3, 4} | |||
| 651 | + | if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected { | |||
| 652 | + | t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected) | |||
| 653 | + | } | |||
| 654 | + | } | |||
| 655 | + | { | |||
| 656 | + | type OneLong struct{ A int64 } | |||
| 657 | + | var ReturnOneLong func(a int64) OneLong | |||
| 658 | + | register(&ReturnOneLong, lib, "ReturnOneLong") | |||
| 659 | + | expected := OneLong{5} | |||
| 660 | + | if ret := ReturnOneLong(5); ret != expected { | |||
| 661 | + | t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected) | |||
| 662 | + | } | |||
| 663 | + | } | |||
| 664 | + | { | |||
| 665 | + | type TwoLongs struct{ A, B int64 } | |||
| 666 | + | var ReturnTwoLongs func(a, b int64) TwoLongs | |||
| 667 | + | register(&ReturnTwoLongs, lib, "ReturnTwoLongs") | |||
| 668 | + | expected := TwoLongs{1, 2} | |||
| 669 | + | if ret := ReturnTwoLongs(1, 2); ret != expected { | |||
| 670 | + | t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected) | |||
| 671 | + | } | |||
| 672 | + | } | |||
| 673 | + | { | |||
| 674 | + | type ThreeLongs struct{ A, B, C int64 } | |||
| 675 | + | var ReturnThreeLongs func(a, b, c int64) ThreeLongs | |||
| 676 | + | register(&ReturnThreeLongs, lib, "ReturnThreeLongs") | |||
| 677 | + | expected := ThreeLongs{1, 2, 3} | |||
| 678 | + | if ret := ReturnThreeLongs(1, 2, 3); ret != expected { | |||
| 679 | + | t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected) | |||
| 680 | + | } | |||
| 681 | + | } | |||
| 682 | + | { | |||
| 683 | + | type OneFloat struct{ A float32 } | |||
| 684 | + | var ReturnOneFloat func(a float32) OneFloat | |||
| 685 | + | register(&ReturnOneFloat, lib, "ReturnOneFloat") | |||
| 686 | + | expected := OneFloat{1} | |||
| 687 | + | if ret := ReturnOneFloat(1); ret != expected { | |||
| 688 | + | t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected) | |||
| 689 | + | } | |||
| 690 | + | } | |||
| 691 | + | { | |||
| 692 | + | type TwoFloats struct{ A, B float32 } | |||
| 693 | + | var ReturnTwoFloats func(a, b float32) TwoFloats | |||
| 694 | + | register(&ReturnTwoFloats, lib, "ReturnTwoFloats") | |||
| 695 | + | expected := TwoFloats{5, 2} | |||
| 696 | + | if ret := ReturnTwoFloats(5, 2); ret != expected { | |||
| 697 | + | t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected) | |||
| 698 | + | } | |||
| 699 | + | } | |||
| 700 | + | { | |||
| 701 | + | type ThreeFloats struct{ A, B, C float32 } | |||
| 702 | + | var ReturnThreeFloats func(a, b, c float32) ThreeFloats | |||
| 703 | + | register(&ReturnThreeFloats, lib, "ReturnThreeFloats") | |||
| 704 | + | expected := ThreeFloats{1, 2, 3} | |||
| 705 | + | if ret := ReturnThreeFloats(1, 2, 3); ret != expected { | |||
| 706 | + | t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected) | |||
| 707 | + | } | |||
| 708 | + | } | |||
| 709 | + | { | |||
| 710 | + | type FourFloats struct{ A, B, C, D float32 } | |||
| 711 | + | var ReturnFourFloats func(a, b, c, d float32) FourFloats | |||
| 712 | + | register(&ReturnFourFloats, lib, "ReturnFourFloats") | |||
| 713 | + | expected := FourFloats{1, 2, 3, 4} | |||
| 714 | + | if ret := ReturnFourFloats(1, 2, 3, 4); ret != expected { | |||
| 715 | + | t.Fatalf("ReturnFourFloats returned %+v wanted %+v", ret, expected) | |||
| 716 | + | } | |||
| 717 | + | } | |||
| 718 | + | { | |||
| 719 | + | type OneDouble struct{ A float64 } | |||
| 720 | + | var ReturnOneDouble func(a float64) OneDouble | |||
| 721 | + | register(&ReturnOneDouble, lib, "ReturnOneDouble") | |||
| 722 | + | expected := OneDouble{1} | |||
| 723 | + | if ret := ReturnOneDouble(1); ret != expected { | |||
| 724 | + | t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected) | |||
| 725 | + | } | |||
| 726 | + | } | |||
| 727 | + | { | |||
| 728 | + | type TwoDoubles struct{ A, B float64 } | |||
| 729 | + | var ReturnTwoDoubles func(a, b float64) TwoDoubles | |||
| 730 | + | register(&ReturnTwoDoubles, lib, "ReturnTwoDoubles") | |||
| 731 | + | expected := TwoDoubles{7, 3} | |||
| 732 | + | if ret := ReturnTwoDoubles(7, 3); ret != expected { | |||
| 733 | + | t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected) | |||
| 734 | + | } | |||
| 735 | + | } | |||
| 736 | + | { | |||
| 737 | + | type ThreeDoubles struct{ A, B, C float64 } | |||
| 738 | + | var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles | |||
| 739 | + | register(&ReturnThreeDoubles, lib, "ReturnThreeDoubles") | |||
| 740 | + | expected := ThreeDoubles{1, 2, 3} | |||
| 741 | + | if ret := ReturnThreeDoubles(1, 2, 3); ret != expected { | |||
| 742 | + | t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected) | |||
| 743 | + | } | |||
| 744 | + | } | |||
| 745 | + | { | |||
| 746 | + | type FourDoubles struct{ A, B, C, D float64 } | |||
| 747 | + | var ReturnFourDoubles func(a, b, c, d float64) FourDoubles | |||
| 748 | + | register(&ReturnFourDoubles, lib, "ReturnFourDoubles") | |||
| 749 | + | expected := FourDoubles{1, 2, 3, 4} | |||
| 750 | + | if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected { | |||
| 751 | + | t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected) | |||
| 752 | + | } | |||
| 753 | + | } | |||
| 754 | + | { | |||
| 755 | + | type FourDoublesInternal struct { | |||
| 756 | + | F struct{ A, B float64 } | |||
| 757 | + | G struct{ C, D float64 } | |||
| 758 | + | } | |||
| 759 | + | var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal | |||
| 760 | + | register(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal") | |||
| 761 | + | expected := FourDoublesInternal{F: struct{ A, B float64 }{A: 1, B: 2}, G: struct{ C, D float64 }{C: 3, D: 4}} | |||
| 762 | + | if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected { | |||
| 763 | + | t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected) | |||
| 764 | + | } | |||
| 765 | + | } | |||
| 766 | + | { | |||
| 767 | + | type FiveDoubles struct{ A, B, C, D, E float64 } | |||
| 768 | + | var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles | |||
| 769 | + | register(&ReturnFiveDoubles, lib, "ReturnFiveDoubles") | |||
| 770 | + | expected := FiveDoubles{1, 2, 3, 4, 5} | |||
| 771 | + | if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected { | |||
| 772 | + | t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected) | |||
| 773 | + | } | |||
| 774 | + | } | |||
| 775 | + | { | |||
| 776 | + | type OneFloatOneDouble struct { | |||
| 777 | + | A float32 | |||
| 778 | + | _ float32 | |||
| 779 | + | B float64 | |||
| 780 | + | } | |||
| 781 | + | var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble | |||
| 782 | + | register(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble") | |||
| 783 | + | expected := OneFloatOneDouble{A: 1, B: 2} | |||
| 784 | + | if ret := ReturnOneFloatOneDouble(1, 2); ret != expected { | |||
| 785 | + | t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected) | |||
| 786 | + | } | |||
| 787 | + | } | |||
| 788 | + | { | |||
| 789 | + | type OneDoubleOneFloat struct { | |||
| 790 | + | A float64 | |||
| 791 | + | B float32 | |||
| 792 | + | } | |||
| 793 | + | var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat | |||
| 794 | + | register(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat") | |||
| 795 | + | expected := OneDoubleOneFloat{1, 2} | |||
| 796 | + | if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected { | |||
| 797 | + | t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected) | |||
| 798 | + | } | |||
| 799 | + | } | |||
| 800 | + | { | |||
| 801 | + | type Unaligned1 struct { | |||
| 802 | + | A int8 | |||
| 803 | + | _ [1]int8 | |||
| 804 | + | B int16 | |||
| 805 | + | _ [1]int32 | |||
| 806 | + | C int64 | |||
| 807 | + | } | |||
| 808 | + | var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1 | |||
| 809 | + | register(&ReturnUnaligned1, lib, "ReturnUnaligned1") | |||
| 810 | + | expected := Unaligned1{A: 1, B: 2, C: 3} | |||
| 811 | + | if ret := ReturnUnaligned1(1, 2, 3); ret != expected { | |||
| 812 | + | t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected) | |||
| 813 | + | } | |||
| 814 | + | } | |||
| 548 | 815 | ||||
| 549 | - | { | 816 | + | { |
| 550 | - | type Empty struct{} | 817 | + | type Mixed1 struct { |
| 551 | - | var ReturnEmpty func() Empty | 818 | + | A float32 |
| 552 | - | purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty") | 819 | + | B int32 |
| 553 | - | ret := ReturnEmpty() | 820 | + | } |
| 554 | - | _ = ret | 821 | + | var ReturnMixed1 func(a float32, b int32) Mixed1 |
| 555 | - | } | 822 | + | register(&ReturnMixed1, lib, "ReturnMixed1") |
| 556 | - | { | 823 | + | expected := Mixed1{1, 2} |
| 557 | - | type inner struct{ a int16 } | 824 | + | if ret := ReturnMixed1(1, 2); ret != expected { |
| 558 | - | type StructInStruct struct { | 825 | + | t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected) |
| 559 | - | a inner | 826 | + | } |
| 560 | - | b inner | 827 | + | } |
| 561 | - | c inner | 828 | + | { |
| 562 | - | } | 829 | + | type Mixed2 struct { |
| 563 | - | var ReturnStructInStruct func(a, b, c int16) StructInStruct | 830 | + | A float32 |
| 564 | - | purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct") | 831 | + | B int32 |
| 565 | - | expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}} | 832 | + | C float32 |
| 566 | - | if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected { | 833 | + | D int32 |
| 567 | - | t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected) | 834 | + | } |
| 568 | - | } | 835 | + | var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2 |
| 569 | - | } | 836 | + | register(&ReturnMixed2, lib, "ReturnMixed2") |
| 570 | - | { | 837 | + | expected := Mixed2{1, 2, 3, 4} |
| 571 | - | type ThreeShorts struct{ a, b, c int16 } | 838 | + | if ret := ReturnMixed2(1, 2, 3, 4); ret != expected { |
| 572 | - | var ReturnThreeShorts func(a, b, c int16) ThreeShorts | 839 | + | t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected) |
| 573 | - | purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts") | 840 | + | } |
| 574 | - | expected := ThreeShorts{^int16(0), 2, 3} | 841 | + | } |
| 575 | - | if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected { | 842 | + | { |
| 576 | - | t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected) | 843 | + | type Mixed3 struct { |
| 577 | - | } | 844 | + | A float32 |
| 578 | - | } | 845 | + | B uint32 |
| 579 | - | { | 846 | + | C float64 |
| 580 | - | type FourShorts struct{ a, b, c, d int16 } | 847 | + | } |
| 581 | - | var ReturnFourShorts func(a, b, c, d int16) FourShorts | 848 | + | var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3 |
| 582 | - | purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts") | 849 | + | register(&ReturnMixed3, lib, "ReturnMixed3") |
| 583 | - | expected := FourShorts{^int16(0), 2, 3, 4} | 850 | + | expected := Mixed3{1, 2, 3} |
| 584 | - | if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected { | 851 | + | if ret := ReturnMixed3(1, 2, 3); ret != expected { |
| 585 | - | t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected) | 852 | + | t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected) |
| 586 | - | } | 853 | + | } |
| 587 | - | } | 854 | + | } |
| 588 | - | { | 855 | + | { |
| 589 | - | type OneLong struct{ a int64 } | 856 | + | type Mixed4 struct { |
| 590 | - | var ReturnOneLong func(a int64) OneLong | 857 | + | A float64 |
| 591 | - | purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong") | 858 | + | B uint32 |
| 592 | - | expected := OneLong{5} | 859 | + | C float32 |
| 593 | - | if ret := ReturnOneLong(5); ret != expected { | 860 | + | } |
| 594 | - | t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected) | 861 | + | var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4 |
| 595 | - | } | 862 | + | register(&ReturnMixed4, lib, "ReturnMixed4") |
| 596 | - | } | 863 | + | expected := Mixed4{1, 2, 3} |
| 597 | - | { | 864 | + | if ret := ReturnMixed4(1, 2, 3); ret != expected { |
| 598 | - | type TwoLongs struct{ a, b int64 } | 865 | + | t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected) |
| 599 | - | var ReturnTwoLongs func(a, b int64) TwoLongs | 866 | + | } |
| 600 | - | purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs") | 867 | + | } |
| 601 | - | expected := TwoLongs{1, 2} | 868 | + | { |
| 602 | - | if ret := ReturnTwoLongs(1, 2); ret != expected { | 869 | + | type Mixed5 struct { |
| 603 | - | t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected) | 870 | + | A *int64 |
| 604 | - | } | 871 | + | B int32 |
| 605 | - | } | 872 | + | C float32 |
| 606 | - | { | 873 | + | D int32 |
| 607 | - | type ThreeLongs struct{ a, b, c int64 } | 874 | + | } |
| 608 | - | var ReturnThreeLongs func(a, b, c int64) ThreeLongs | 875 | + | var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5 |
| 609 | - | purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs") | 876 | + | register(&ReturnMixed5, lib, "ReturnMixed5") |
| 610 | - | expected := ThreeLongs{1, 2, 3} | 877 | + | ptr := new(int64) |
| 611 | - | if ret := ReturnThreeLongs(1, 2, 3); ret != expected { | 878 | + | expected := Mixed5{ptr, 1, 7.2, 9} |
| 612 | - | t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected) | 879 | + | if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected { |
| 613 | - | } | 880 | + | t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected) |
| 614 | - | } | 881 | + | } |
| 615 | - | { | 882 | + | runtime.KeepAlive(ptr) |
| 616 | - | type OneFloat struct{ a float32 } | 883 | + | } |
| 617 | - | var ReturnOneFloat func(a float32) OneFloat | 884 | + | { |
| 618 | - | purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat") | 885 | + | type Mixed5 struct { |
| 619 | - | expected := OneFloat{1} | 886 | + | A *int64 |
| 620 | - | if ret := ReturnOneFloat(1); ret != expected { | 887 | + | B int32 |
| 621 | - | t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected) | 888 | + | C float32 |
| 622 | - | } | 889 | + | D int32 |
| 623 | - | } | 890 | + | } |
| 624 | - | { | 891 | + | var IdentityMixed5 func(m Mixed5) Mixed5 |
| 625 | - | type TwoFloats struct{ a, b float32 } | 892 | + | // TODO: when struct args are supported in NewCallback use register() instead |
| 626 | - | var ReturnTwoFloats func(a, b float32) TwoFloats | 893 | + | purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5") |
| 627 | - | purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats") | 894 | + | ptr := new(int64) |
| 628 | - | expected := TwoFloats{3, 10} | 895 | + | expected := Mixed5{ptr, 1, 7.2, 9} |
| 629 | - | if ret := ReturnTwoFloats(5, 2); ret != expected { | 896 | + | if ret := IdentityMixed5(expected); ret != expected { |
| 630 | - | t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected) | 897 | + | t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected) |
| 631 | - | } | 898 | + | } |
| 632 | - | } | 899 | + | runtime.KeepAlive(ptr) |
| 633 | - | { | 900 | + | } |
| 634 | - | type ThreeFloats struct{ a, b, c float32 } | 901 | + | { |
| 635 | - | var ReturnThreeFloats func(a, b, c float32) ThreeFloats | 902 | + | type SmallBool struct { |
| 636 | - | purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats") | 903 | + | A bool |
| 637 | - | expected := ThreeFloats{1, 2, 3} | 904 | + | B int32 |
| 638 | - | if ret := ReturnThreeFloats(1, 2, 3); ret != expected { | 905 | + | C int64 |
| 639 | - | t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected) | 906 | + | } |
| 640 | - | } | 907 | + | var ReturnSmallBool func(a bool, b int32, c int64) SmallBool |
| 641 | - | } | 908 | + | register(&ReturnSmallBool, lib, "ReturnSmallBool") |
| 642 | - | { | 909 | + | expected := SmallBool{true, 42, 123456789} |
| 643 | - | type OneDouble struct{ a float64 } | 910 | + | if ret := ReturnSmallBool(true, 42, 123456789); ret != expected { |
| 644 | - | var ReturnOneDouble func(a float64) OneDouble | 911 | + | t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected) |
| 645 | - | purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble") | 912 | + | } |
| 646 | - | expected := OneDouble{1} | 913 | + | } |
| 647 | - | if ret := ReturnOneDouble(1); ret != expected { | 914 | + | { |
| 648 | - | t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected) | 915 | + | type LargeBool struct { |
| 649 | - | } | 916 | + | A bool |
| 650 | - | } | 917 | + | B int32 |
| 651 | - | { | 918 | + | C int64 |
| 652 | - | type TwoDoubles struct{ a, b float64 } | 919 | + | D int64 |
| 653 | - | var ReturnTwoDoubles func(a, b float64) TwoDoubles | 920 | + | } |
| 654 | - | purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles") | 921 | + | var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool |
| 655 | - | expected := TwoDoubles{1, 2} | 922 | + | register(&ReturnLargeBool, lib, "ReturnLargeBool") |
| 656 | - | if ret := ReturnTwoDoubles(1, 2); ret != expected { | 923 | + | expected := LargeBool{false, -99, 987654321, 111222333444} |
| 657 | - | t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected) | 924 | + | if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected { |
| 658 | - | } | 925 | + | t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected) |
| 659 | - | } | 926 | + | } |
| 660 | - | { | 927 | + | } |
| 661 | - | type ThreeDoubles struct{ a, b, c float64 } | 928 | + | { |
| 662 | - | var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles | 929 | + | type Ptr1 struct { |
| 663 | - | purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles") | 930 | + | a *int64 |
| 664 | - | expected := ThreeDoubles{1, 2, 3} | 931 | + | b unsafe.Pointer |
| 665 | - | if ret := ReturnThreeDoubles(1, 2, 3); ret != expected { | 932 | + | } |
| 666 | - | t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected) | 933 | + | var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1 |
| 667 | - | } | 934 | + | purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1") |
| 668 | - | } | 935 | + | a, b := new(int64), new(struct{}) |
| 669 | - | { | 936 | + | expected := Ptr1{a, unsafe.Pointer(b)} |
| 670 | - | type FourDoubles struct{ a, b, c, d float64 } | 937 | + | if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected { |
| 671 | - | var ReturnFourDoubles func(a, b, c, d float64) FourDoubles | 938 | + | t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected) |
| 672 | - | purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles") | 939 | + | } |
| 673 | - | expected := FourDoubles{1, 2, 3, 4} | 940 | + | runtime.KeepAlive(a) |
| 674 | - | if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected { | 941 | + | runtime.KeepAlive(b) |
| 675 | - | t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected) | 942 | + | } |
| 676 | - | } | 943 | + | }) |
| 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 | } | 944 | } | ||
| 865 | } | 945 | } | ||
M sys_amd64.s +7 -2
| @@ -140,7 +140,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 140 | 140 | SUBQ $(24+callbackArgs__size), SP | |
| 141 | 141 | MOVQ AX, (24+callbackArgs_index)(SP) // callback index | |
| 142 | 142 | MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector | |
| 143 | - | MOVQ $0, (24+callbackArgs_result)(SP) // result | |
| 143 | + | MOVQ DI, (24+callbackArgs_result)(SP) // result | |
| 144 | 144 | LEAQ 24(SP), AX // take the address of callbackArgs | |
| 145 | 145 | ||
| 146 | 146 | // Call cgocallback, which will call callbackWrap(frame). | |
| @@ -153,7 +153,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 153 | 153 | ||
| 154 | 154 | // Get callback result. | |
| 155 | 155 | MOVQ (24+callbackArgs_result)(SP), AX | |
| 156 | - | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | |
| 156 | + | MOVQ (24+callbackArgs_result+8)(SP), DX | |
| 157 | + | // Restore XMM0/XMM1 from result[2]/result[3] for struct eightbytes | |
| 158 | + | // classified as SSE by the SysV ABI. | |
| 159 | + | MOVQ (24+callbackArgs_result+16)(SP), X0 | |
| 160 | + | MOVQ (24+callbackArgs_result+24)(SP), X1 | |
| 161 | + | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | |
| 157 | 162 | ||
| 158 | 163 | POP_REGS_HOST_TO_ABI0() | |
| 159 | 164 | ||
| @@ -140,7 +140,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 140 | SUBQ $(24+callbackArgs__size), SP | 140 | SUBQ $(24+callbackArgs__size), SP | ||
| 141 | MOVQ AX, (24+callbackArgs_index)(SP) // callback index | 141 | MOVQ AX, (24+callbackArgs_index)(SP) // callback index | ||
| 142 | MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector | 142 | MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector | ||
| 143 | - | MOVQ $0, (24+callbackArgs_result)(SP) // result | 143 | + | MOVQ DI, (24+callbackArgs_result)(SP) // result |
| 144 | LEAQ 24(SP), AX // take the address of callbackArgs | 144 | LEAQ 24(SP), AX // take the address of callbackArgs | ||
| 145 | 145 | ||||
| 146 | // Call cgocallback, which will call callbackWrap(frame). | 146 | // Call cgocallback, which will call callbackWrap(frame). | ||
| @@ -153,7 +153,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 153 | 153 | ||||
| 154 | // Get callback result. | 154 | // Get callback result. | ||
| 155 | MOVQ (24+callbackArgs_result)(SP), AX | 155 | MOVQ (24+callbackArgs_result)(SP), AX | ||
| 156 | - | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | 156 | + | MOVQ (24+callbackArgs_result+8)(SP), DX |
| 157 | + | // Restore XMM0/XMM1 from result[2]/result[3] for struct eightbytes | |||
| 158 | + | // classified as SSE by the SysV ABI. | |||
| 159 | + | MOVQ (24+callbackArgs_result+16)(SP), X0 | |||
| 160 | + | MOVQ (24+callbackArgs_result+24)(SP), X1 | |||
| 161 | + | ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct | |||
| 157 | 162 | ||||
| 158 | POP_REGS_HOST_TO_ABI0() | 163 | POP_REGS_HOST_TO_ABI0() | ||
| 159 | 164 | ||||
M sys_unix_arm64.s +8 -3
| @@ -47,7 +47,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 47 | 47 | MOVD $(callbackArgs__size)(RSP), R13 | |
| 48 | 48 | MOVD R12, callbackArgs_index(R13) // callback index | |
| 49 | 49 | MOVD R14, callbackArgs_args(R13) // address of args vector | |
| 50 | - | MOVD ZR, callbackArgs_result(R13) // result | |
| 50 | + | MOVD R8, callbackArgs_result(R13) // result | |
| 51 | 51 | ||
| 52 | 52 | // Move parameters into registers | |
| 53 | 53 | // Get the ABIInternal function pointer | |
| @@ -60,8 +60,13 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 60 | 60 | BL crosscall2(SB) | |
| 61 | 61 | ||
| 62 | 62 | // Get callback result. | |
| 63 | - | MOVD $(callbackArgs__size)(RSP), R13 | |
| 64 | - | MOVD callbackArgs_result(R13), R0 | |
| 63 | + | MOVD $(callbackArgs__size)(RSP), R13 | |
| 64 | + | MOVD (callbackArgs_result+0)(R13), R0 | |
| 65 | + | MOVD (callbackArgs_result+8)(R13), R1 | |
| 66 | + | FMOVD (callbackArgs_result+0)(R13), F0 | |
| 67 | + | FMOVD (callbackArgs_result+8)(R13), F1 | |
| 68 | + | FMOVD (callbackArgs_result+16)(R13), F2 | |
| 69 | + | FMOVD (callbackArgs_result+24)(R13), F3 | |
| 65 | 70 | ||
| 66 | 71 | // Restore LR and R27 | |
| 67 | 72 | LDP 0(RSP), (R27, R30) | |
| @@ -47,7 +47,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 47 | MOVD $(callbackArgs__size)(RSP), R13 | 47 | MOVD $(callbackArgs__size)(RSP), R13 | ||
| 48 | MOVD R12, callbackArgs_index(R13) // callback index | 48 | MOVD R12, callbackArgs_index(R13) // callback index | ||
| 49 | MOVD R14, callbackArgs_args(R13) // address of args vector | 49 | MOVD R14, callbackArgs_args(R13) // address of args vector | ||
| 50 | - | MOVD ZR, callbackArgs_result(R13) // result | 50 | + | MOVD R8, callbackArgs_result(R13) // result |
| 51 | 51 | ||||
| 52 | // Move parameters into registers | 52 | // Move parameters into registers | ||
| 53 | // Get the ABIInternal function pointer | 53 | // Get the ABIInternal function pointer | ||
| @@ -60,8 +60,13 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 60 | BL crosscall2(SB) | 60 | BL crosscall2(SB) | ||
| 61 | 61 | ||||
| 62 | // Get callback result. | 62 | // Get callback result. | ||
| 63 | - | MOVD $(callbackArgs__size)(RSP), R13 | 63 | + | MOVD $(callbackArgs__size)(RSP), R13 |
| 64 | - | MOVD callbackArgs_result(R13), R0 | 64 | + | MOVD (callbackArgs_result+0)(R13), R0 |
| 65 | + | MOVD (callbackArgs_result+8)(R13), R1 | |||
| 66 | + | FMOVD (callbackArgs_result+0)(R13), F0 | |||
| 67 | + | FMOVD (callbackArgs_result+8)(R13), F1 | |||
| 68 | + | FMOVD (callbackArgs_result+16)(R13), F2 | |||
| 69 | + | FMOVD (callbackArgs_result+24)(R13), F3 | |||
| 65 | 70 | ||||
| 66 | // Restore LR and R27 | 71 | // Restore LR and R27 | ||
| 67 | LDP 0(RSP), (R27, R30) | 72 | LDP 0(RSP), (R27, R30) | ||
M sys_unix_loong64.s +9 -5
| @@ -30,7 +30,10 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 30 | 30 | MOVV R11, 120(R14) | |
| 31 | 31 | ||
| 32 | 32 | // Adjust SP by frame size. | |
| 33 | - | SUBV $(22*8), R3 | |
| 33 | + | // Layout: R1(8) + R30(8) + callbackArgs(48) = 64 bytes below R14, | |
| 34 | + | // plus 128 bytes of saved registers above = 192 = 24*8. | |
| 35 | + | // callbackArgs is placed at SP+16, ending at SP+64 = R14. | |
| 36 | + | SUBV $(24*8), R3 | |
| 34 | 37 | ||
| 35 | 38 | // It is important to save R30 because the go assembler | |
| 36 | 39 | // uses it for move instructions for a variable. | |
| @@ -47,8 +50,9 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 47 | 50 | MOVV R1, 0(R3) | |
| 48 | 51 | MOVV R30, 8(R3) | |
| 49 | 52 | ||
| 50 | - | // Create a struct callbackArgs on our stack. | |
| 51 | - | MOVV $(callbackArgs__size)(R3), R13 | |
| 53 | + | // Create a struct callbackArgs on our stack at SP+16 | |
| 54 | + | // (right after the saved R1 and R30). | |
| 55 | + | MOVV $16(R3), R13 | |
| 52 | 56 | MOVV R12, callbackArgs_index(R13) // callback index | |
| 53 | 57 | MOVV R14, callbackArgs_args(R13) // address of args vector | |
| 54 | 58 | MOVV $0, callbackArgs_result(R13) // result | |
| @@ -64,12 +68,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 64 | 68 | JAL crosscall2(SB) | |
| 65 | 69 | ||
| 66 | 70 | // Get callback result. | |
| 67 | - | MOVV $(callbackArgs__size)(R3), R13 | |
| 71 | + | MOVV $16(R3), R13 | |
| 68 | 72 | MOVV callbackArgs_result(R13), R4 | |
| 69 | 73 | ||
| 70 | 74 | // Restore LR and R30 | |
| 71 | 75 | MOVV 0(R3), R1 | |
| 72 | 76 | MOVV 8(R3), R30 | |
| 73 | - | ADDV $(22*8), R3 | |
| 77 | + | ADDV $(24*8), R3 | |
| 74 | 78 | ||
| 75 | 79 | RET | |
| @@ -30,7 +30,10 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 30 | MOVV R11, 120(R14) | 30 | MOVV R11, 120(R14) | ||
| 31 | 31 | ||||
| 32 | // Adjust SP by frame size. | 32 | // Adjust SP by frame size. | ||
| 33 | - | SUBV $(22*8), R3 | 33 | + | // Layout: R1(8) + R30(8) + callbackArgs(48) = 64 bytes below R14, |
| 34 | + | // plus 128 bytes of saved registers above = 192 = 24*8. | |||
| 35 | + | // callbackArgs is placed at SP+16, ending at SP+64 = R14. | |||
| 36 | + | SUBV $(24*8), R3 | |||
| 34 | 37 | ||||
| 35 | // It is important to save R30 because the go assembler | 38 | // It is important to save R30 because the go assembler | ||
| 36 | // uses it for move instructions for a variable. | 39 | // uses it for move instructions for a variable. | ||
| @@ -47,8 +50,9 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 47 | MOVV R1, 0(R3) | 50 | MOVV R1, 0(R3) | ||
| 48 | MOVV R30, 8(R3) | 51 | MOVV R30, 8(R3) | ||
| 49 | 52 | ||||
| 50 | - | // Create a struct callbackArgs on our stack. | 53 | + | // Create a struct callbackArgs on our stack at SP+16 |
| 51 | - | MOVV $(callbackArgs__size)(R3), R13 | 54 | + | // (right after the saved R1 and R30). |
| 55 | + | MOVV $16(R3), R13 | |||
| 52 | MOVV R12, callbackArgs_index(R13) // callback index | 56 | MOVV R12, callbackArgs_index(R13) // callback index | ||
| 53 | MOVV R14, callbackArgs_args(R13) // address of args vector | 57 | MOVV R14, callbackArgs_args(R13) // address of args vector | ||
| 54 | MOVV $0, callbackArgs_result(R13) // result | 58 | MOVV $0, callbackArgs_result(R13) // result | ||
| @@ -64,12 +68,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 64 | JAL crosscall2(SB) | 68 | JAL crosscall2(SB) | ||
| 65 | 69 | ||||
| 66 | // Get callback result. | 70 | // Get callback result. | ||
| 67 | - | MOVV $(callbackArgs__size)(R3), R13 | 71 | + | MOVV $16(R3), R13 |
| 68 | MOVV callbackArgs_result(R13), R4 | 72 | MOVV callbackArgs_result(R13), R4 | ||
| 69 | 73 | ||||
| 70 | // Restore LR and R30 | 74 | // Restore LR and R30 | ||
| 71 | MOVV 0(R3), R1 | 75 | MOVV 0(R3), R1 | ||
| 72 | MOVV 8(R3), R30 | 76 | MOVV 8(R3), R30 | ||
| 73 | - | ADDV $(22*8), R3 | 77 | + | ADDV $(24*8), R3 |
| 74 | 78 | ||||
| 75 | RET | 79 | RET | ||
M syscall_notstackargs.go +1 -1
| @@ -29,7 +29,7 @@ type callbackArgs struct { | |||
| 29 | 29 | // for this callback. | |
| 30 | 30 | args unsafe.Pointer | |
| 31 | 31 | // Below are out-args from callbackWrap | |
| 32 | - | result uintptr | |
| 32 | + | result [4]uintptr | |
| 33 | 33 | } | |
| 34 | 34 | ||
| 35 | 35 | func (c *callbackArgs) stackFrame() unsafe.Pointer { | |
| @@ -29,7 +29,7 @@ type callbackArgs struct { | |||||
| 29 | // for this callback. | 29 | // for this callback. | ||
| 30 | args unsafe.Pointer | 30 | args unsafe.Pointer | ||
| 31 | // Below are out-args from callbackWrap | 31 | // Below are out-args from callbackWrap | ||
| 32 | - | result uintptr | 32 | + | result [4]uintptr |
| 33 | } | 33 | } | ||
| 34 | 34 | ||||
| 35 | func (c *callbackArgs) stackFrame() unsafe.Pointer { | 35 | func (c *callbackArgs) stackFrame() unsafe.Pointer { | ||
M syscall_stackargs.go +1 -1
| @@ -29,7 +29,7 @@ type callbackArgs struct { | |||
| 29 | 29 | // for this callback. | |
| 30 | 30 | args unsafe.Pointer | |
| 31 | 31 | // Below are out-args from callbackWrap | |
| 32 | - | result uintptr | |
| 32 | + | result [1]uintptr | |
| 33 | 33 | // stackArgs points to stack-passed arguments for architectures where | |
| 34 | 34 | // they can't be made contiguous with register args (e.g., ppc64le). | |
| 35 | 35 | // On other architectures, this is nil and stack args are read from | |
| @@ -29,7 +29,7 @@ type callbackArgs struct { | |||||
| 29 | // for this callback. | 29 | // for this callback. | ||
| 30 | args unsafe.Pointer | 30 | args unsafe.Pointer | ||
| 31 | // Below are out-args from callbackWrap | 31 | // Below are out-args from callbackWrap | ||
| 32 | - | result uintptr | 32 | + | result [1]uintptr |
| 33 | // stackArgs points to stack-passed arguments for architectures where | 33 | // stackArgs points to stack-passed arguments for architectures where | ||
| 34 | // they can't be made contiguous with register args (e.g., ppc64le). | 34 | // they can't be made contiguous with register args (e.g., ppc64le). | ||
| 35 | // On other architectures, this is nil and stack args are read from | 35 | // On other architectures, this is nil and stack args are read from | ||
M syscall_unix.go +18 -7
| @@ -76,6 +76,7 @@ func compileCallback(fn any) uintptr { | |||
| 76 | 76 | if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) { | |
| 77 | 77 | continue | |
| 78 | 78 | } | |
| 79 | + | ensureStructSupported() | |
| 79 | 80 | fallthrough | |
| 80 | 81 | case reflect.Interface, reflect.Func, reflect.Slice, | |
| 81 | 82 | reflect.Chan, reflect.Complex64, reflect.Complex128, | |
| @@ -89,7 +90,7 @@ output: | |||
| 89 | 90 | switch ty.Out(0).Kind() { | |
| 90 | 91 | case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | |
| 91 | 92 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, | |
| 92 | - | reflect.Bool, reflect.UnsafePointer: | |
| 93 | + | reflect.Bool, reflect.UnsafePointer, reflect.Struct: | |
| 93 | 94 | break output | |
| 94 | 95 | } | |
| 95 | 96 | panic("purego: unsupported return type: " + ty.String()) | |
| @@ -142,6 +143,14 @@ func callbackWrap(a *callbackArgs) { | |||
| 142 | 143 | // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers). | |
| 143 | 144 | var floatsN int | |
| 144 | 145 | var intsN int | |
| 146 | + | // On amd64/loong64/ppc64le/riscv64/s390x, when returning a struct larger than | |
| 147 | + | // maxRegAllocStructSize, the caller passes a hidden pointer in the first integer | |
| 148 | + | // register. Skip it to avoid misreading it as the first function argument. | |
| 149 | + | if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && | |
| 150 | + | fnType.NumOut() == 1 && fnType.Out(0).Kind() == reflect.Struct && | |
| 151 | + | fnType.Out(0).Size() > maxRegAllocStructSize { | |
| 152 | + | intsN = 1 | |
| 153 | + | } | |
| 145 | 154 | // stackSlot points to the index into frame (or stackFrame) of the current stack element. | |
| 146 | 155 | // When stackFrame is nil, stack begins after float and integer registers in frame. | |
| 147 | 156 | // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0. | |
| @@ -225,19 +234,21 @@ func callbackWrap(a *callbackArgs) { | |||
| 225 | 234 | if len(ret) > 0 { | |
| 226 | 235 | switch k := ret[0].Kind(); k { | |
| 227 | 236 | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | |
| 228 | - | a.result = uintptr(ret[0].Uint()) | |
| 237 | + | a.result[0] = uintptr(ret[0].Uint()) | |
| 229 | 238 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: | |
| 230 | - | a.result = uintptr(ret[0].Int()) | |
| 239 | + | a.result[0] = uintptr(ret[0].Int()) | |
| 231 | 240 | case reflect.Bool: | |
| 232 | 241 | if ret[0].Bool() { | |
| 233 | - | a.result = 1 | |
| 242 | + | a.result[0] = 1 | |
| 234 | 243 | } else { | |
| 235 | - | a.result = 0 | |
| 244 | + | a.result[0] = 0 | |
| 236 | 245 | } | |
| 237 | 246 | case reflect.Pointer: | |
| 238 | - | a.result = ret[0].Pointer() | |
| 247 | + | a.result[0] = ret[0].Pointer() | |
| 239 | 248 | case reflect.UnsafePointer: | |
| 240 | - | a.result = ret[0].Pointer() | |
| 249 | + | a.result[0] = ret[0].Pointer() | |
| 250 | + | case reflect.Struct: | |
| 251 | + | setStruct(a, ret[0]) | |
| 241 | 252 | default: | |
| 242 | 253 | panic("purego: unsupported kind: " + k.String()) | |
| 243 | 254 | } | |
| @@ -76,6 +76,7 @@ func compileCallback(fn any) uintptr { | |||||
| 76 | if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) { | 76 | if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) { | ||
| 77 | continue | 77 | continue | ||
| 78 | } | 78 | } | ||
| 79 | + | ensureStructSupported() | |||
| 79 | fallthrough | 80 | fallthrough | ||
| 80 | case reflect.Interface, reflect.Func, reflect.Slice, | 81 | case reflect.Interface, reflect.Func, reflect.Slice, | ||
| 81 | reflect.Chan, reflect.Complex64, reflect.Complex128, | 82 | reflect.Chan, reflect.Complex64, reflect.Complex128, | ||
| @@ -89,7 +90,7 @@ output: | |||||
| 89 | switch ty.Out(0).Kind() { | 90 | switch ty.Out(0).Kind() { | ||
| 90 | case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | 91 | case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | ||
| 91 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, | 92 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, | ||
| 92 | - | reflect.Bool, reflect.UnsafePointer: | 93 | + | reflect.Bool, reflect.UnsafePointer, reflect.Struct: |
| 93 | break output | 94 | break output | ||
| 94 | } | 95 | } | ||
| 95 | panic("purego: unsupported return type: " + ty.String()) | 96 | panic("purego: unsupported return type: " + ty.String()) | ||
| @@ -142,6 +143,14 @@ func callbackWrap(a *callbackArgs) { | |||||
| 142 | // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers). | 143 | // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers). | ||
| 143 | var floatsN int | 144 | var floatsN int | ||
| 144 | var intsN int | 145 | var intsN int | ||
| 146 | + | // On amd64/loong64/ppc64le/riscv64/s390x, when returning a struct larger than | |||
| 147 | + | // maxRegAllocStructSize, the caller passes a hidden pointer in the first integer | |||
| 148 | + | // register. Skip it to avoid misreading it as the first function argument. | |||
| 149 | + | if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && | |||
| 150 | + | fnType.NumOut() == 1 && fnType.Out(0).Kind() == reflect.Struct && | |||
| 151 | + | fnType.Out(0).Size() > maxRegAllocStructSize { | |||
| 152 | + | intsN = 1 | |||
| 153 | + | } | |||
| 145 | // stackSlot points to the index into frame (or stackFrame) of the current stack element. | 154 | // stackSlot points to the index into frame (or stackFrame) of the current stack element. | ||
| 146 | // When stackFrame is nil, stack begins after float and integer registers in frame. | 155 | // When stackFrame is nil, stack begins after float and integer registers in frame. | ||
| 147 | // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0. | 156 | // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0. | ||
| @@ -225,19 +234,21 @@ func callbackWrap(a *callbackArgs) { | |||||
| 225 | if len(ret) > 0 { | 234 | if len(ret) > 0 { | ||
| 226 | switch k := ret[0].Kind(); k { | 235 | switch k := ret[0].Kind(); k { | ||
| 227 | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | 236 | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | ||
| 228 | - | a.result = uintptr(ret[0].Uint()) | 237 | + | a.result[0] = uintptr(ret[0].Uint()) |
| 229 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: | 238 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: | ||
| 230 | - | a.result = uintptr(ret[0].Int()) | 239 | + | a.result[0] = uintptr(ret[0].Int()) |
| 231 | case reflect.Bool: | 240 | case reflect.Bool: | ||
| 232 | if ret[0].Bool() { | 241 | if ret[0].Bool() { | ||
| 233 | - | a.result = 1 | 242 | + | a.result[0] = 1 |
| 234 | } else { | 243 | } else { | ||
| 235 | - | a.result = 0 | 244 | + | a.result[0] = 0 |
| 236 | } | 245 | } | ||
| 237 | case reflect.Pointer: | 246 | case reflect.Pointer: | ||
| 238 | - | a.result = ret[0].Pointer() | 247 | + | a.result[0] = ret[0].Pointer() |
| 239 | case reflect.UnsafePointer: | 248 | case reflect.UnsafePointer: | ||
| 240 | - | a.result = ret[0].Pointer() | 249 | + | a.result[0] = ret[0].Pointer() |
| 250 | + | case reflect.Struct: | |||
| 251 | + | setStruct(a, ret[0]) | |||
| 241 | default: | 252 | default: | ||
| 242 | panic("purego: unsupported kind: " + k.String()) | 253 | panic("purego: unsupported kind: " + k.String()) | ||
| 243 | } | 254 | } | ||
M testdata/structtest/structreturn_test.c +10 -1
| @@ -75,7 +75,7 @@ struct TwoFloats{ | |||
| 75 | 75 | }; | |
| 76 | 76 | ||
| 77 | 77 | struct TwoFloats ReturnTwoFloats(float a, float b) { | |
| 78 | - | struct TwoFloats e = {a-b, a*b}; | |
| 78 | + | struct TwoFloats e = {a, b}; | |
| 79 | 79 | return e; | |
| 80 | 80 | } | |
| 81 | 81 | ||
| @@ -88,6 +88,15 @@ struct ThreeFloats ReturnThreeFloats(float a, float b, float c) { | |||
| 88 | 88 | return e; | |
| 89 | 89 | } | |
| 90 | 90 | ||
| 91 | + | struct FourFloats{ | |
| 92 | + | float a, b, c, d; | |
| 93 | + | }; | |
| 94 | + | ||
| 95 | + | struct FourFloats ReturnFourFloats(float a, float b, float c, float d) { | |
| 96 | + | struct FourFloats e = {a, b, c, d}; | |
| 97 | + | return e; | |
| 98 | + | } | |
| 99 | + | ||
| 91 | 100 | struct OneFloat ReturnOneFloat(float a) { | |
| 92 | 101 | struct OneFloat e = {a}; | |
| 93 | 102 | return e; | |
| @@ -75,7 +75,7 @@ struct TwoFloats{ | |||||
| 75 | }; | 75 | }; | ||
| 76 | 76 | ||||
| 77 | struct TwoFloats ReturnTwoFloats(float a, float b) { | 77 | struct TwoFloats ReturnTwoFloats(float a, float b) { | ||
| 78 | - | struct TwoFloats e = {a-b, a*b}; | 78 | + | struct TwoFloats e = {a, b}; |
| 79 | return e; | 79 | return e; | ||
| 80 | } | 80 | } | ||
| 81 | 81 | ||||
| @@ -88,6 +88,15 @@ struct ThreeFloats ReturnThreeFloats(float a, float b, float c) { | |||||
| 88 | return e; | 88 | return e; | ||
| 89 | } | 89 | } | ||
| 90 | 90 | ||||
| 91 | + | struct FourFloats{ | |||
| 92 | + | float a, b, c, d; | |||
| 93 | + | }; | |||
| 94 | + | ||||
| 95 | + | struct FourFloats ReturnFourFloats(float a, float b, float c, float d) { | |||
| 96 | + | struct FourFloats e = {a, b, c, d}; | |||
| 97 | + | return e; | |||
| 98 | + | } | |||
| 99 | + | ||||
| 91 | struct OneFloat ReturnOneFloat(float a) { | 100 | struct OneFloat ReturnOneFloat(float a) { | ||
| 92 | struct OneFloat e = {a}; | 101 | struct OneFloat e = {a}; | ||
| 93 | return e; | 102 | return e; | ||