purego: support struct arguments in callbacks (#437)
Add getCallbackStruct implementations for amd64 and arm64 to decode struct arguments from the callback frame. Update callbackWrap to call getCallbackStruct instead of treating all struct arguments as CDecl. The struct argument tests are restructured to run each test case with both RegisterLibFunc (calling C) and GoCallbackFunc (Go callback via NewCallback), ensuring round-trip correctness through the callback path. Updates #225
- commit
- 1a798da253ebeb936ca1f317d2d640ac4cdad0d7
- author
- Hajime Hoshi · · 2026-03-11 15:29 UTC
- committer
- GitHub · · 2026-03-11 15:29 UTC
- parent
- 6df3e6f
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown
12 files · +1117 -486
M struct_386.go +8 -1
| @@ -3,7 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | 4 | package purego | |
| 5 | 5 | ||
| 6 | - | import "reflect" | |
| 6 | + | import ( | |
| 7 | + | "reflect" | |
| 8 | + | "unsafe" | |
| 9 | + | ) | |
| 7 | 10 | ||
| 8 | 11 | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any { | |
| 9 | 12 | panic("purego: struct arguments are not supported") | |
| @@ -40,6 +43,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 40 | 43 | panic("purego: bundleStackArgs should not be called on 386") | |
| 41 | 44 | } | |
| 42 | 45 | ||
| 46 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 47 | + | panic("purego: struct callback arguments are not supported on 386") | |
| 48 | + | } | |
| 49 | + | ||
| 43 | 50 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 44 | 51 | panic("purego: struct returns are not supported on 386") | |
| 45 | 52 | } | |
| @@ -3,7 +3,10 @@ | |||||
| 3 | 3 | ||||
| 4 | package purego | 4 | package purego | ||
| 5 | 5 | ||||
| 6 | - | import "reflect" | 6 | + | import ( |
| 7 | + | "reflect" | |||
| 8 | + | "unsafe" | |||
| 9 | + | ) | |||
| 7 | 10 | ||||
| 8 | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any { | 11 | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any { | ||
| 9 | panic("purego: struct arguments are not supported") | 12 | panic("purego: struct arguments are not supported") | ||
| @@ -40,6 +43,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 40 | panic("purego: bundleStackArgs should not be called on 386") | 43 | panic("purego: bundleStackArgs should not be called on 386") | ||
| 41 | } | 44 | } | ||
| 42 | 45 | ||||
| 46 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 47 | + | panic("purego: struct callback arguments are not supported on 386") | |||
| 48 | + | } | |||
| 49 | + | ||||
| 43 | func setStruct(a *callbackArgs, ret reflect.Value) { | 50 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 44 | panic("purego: struct returns are not supported on 386") | 51 | panic("purego: struct returns are not supported on 386") | ||
| 45 | } | 52 | } | ||
M struct_amd64.go +78 -0
| @@ -6,6 +6,7 @@ package purego | |||
| 6 | 6 | import ( | |
| 7 | 7 | "math" | |
| 8 | 8 | "reflect" | |
| 9 | + | "runtime" | |
| 9 | 10 | "unsafe" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| @@ -285,6 +286,83 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 285 | 286 | panic("purego: bundleStackArgs should not be called on amd64") | |
| 286 | 287 | } | |
| 287 | 288 | ||
| 289 | + | // getCallbackStruct reads a struct argument from the callback frame on amd64. | |
| 290 | + | // It mirrors the SysV AMD64 ABI rules used by addStruct for the Go→C path. | |
| 291 | + | // | |
| 292 | + | // getCallbackStruct is only used on Unix. On Windows, callbacks are handled by | |
| 293 | + | // the runtime's own callback mechanism, so this function is compiled but unused. | |
| 294 | + | // | |
| 295 | + | // SysV AMD64 struct argument passing rules: | |
| 296 | + | // - Struct > 16 bytes (postMerger): passed on the stack as raw bytes | |
| 297 | + | // - Struct ≤ 16 bytes: classify each eightbyte (INTEGER or SSE), | |
| 298 | + | // read from the appropriate register class | |
| 299 | + | // - If not enough registers for all eightbytes: entire struct goes on the stack | |
| 300 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 301 | + | switch runtime.GOOS { | |
| 302 | + | case "darwin", "freebsd", "linux", "netbsd": | |
| 303 | + | default: | |
| 304 | + | panic("purego: getCallbackStruct is not supported on " + runtime.GOOS) | |
| 305 | + | } | |
| 306 | + | ||
| 307 | + | f := (*[callbackMaxFrame]uintptr)(frame) | |
| 308 | + | size := inType.Size() | |
| 309 | + | ||
| 310 | + | // Structs > 16 bytes are passed on the stack as raw bytes (SysV ABI MEMORY class). | |
| 311 | + | if postMerger(inType) { | |
| 312 | + | numSlots := int((size + 7) / 8) | |
| 313 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |
| 314 | + | *stackSlot += numSlots | |
| 315 | + | return v | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | // Struct ≤ 16 bytes: classify each eightbyte and read from the appropriate register. | |
| 319 | + | numEightbytes := int((size + 7) / 8) | |
| 320 | + | ||
| 321 | + | // Count how many integer and SSE registers this struct needs. | |
| 322 | + | var needInts, needFloats int | |
| 323 | + | for i := 0; i < numEightbytes; i++ { | |
| 324 | + | class := classifyEightbyte(inType, uintptr(i)*8, uintptr(i)*8+8) | |
| 325 | + | if class == _SSE { | |
| 326 | + | needFloats++ | |
| 327 | + | } else { | |
| 328 | + | needInts++ | |
| 329 | + | } | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | // If not enough registers for all eightbytes, the entire struct goes on the stack. | |
| 333 | + | if *intsN+needInts > numOfIntegerRegisters() || *floatsN+needFloats > numOfFloatRegisters() { | |
| 334 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |
| 335 | + | *stackSlot += numEightbytes | |
| 336 | + | return v | |
| 337 | + | } | |
| 338 | + | ||
| 339 | + | // Read each eightbyte from its appropriate register class. | |
| 340 | + | var r1, r2 uintptr | |
| 341 | + | for i := 0; i < numEightbytes; i++ { | |
| 342 | + | class := classifyEightbyte(inType, uintptr(i)*8, uintptr(i)*8+8) | |
| 343 | + | if class == _SSE { | |
| 344 | + | if i == 0 { | |
| 345 | + | r1 = f[*floatsN] | |
| 346 | + | } else { | |
| 347 | + | r2 = f[*floatsN] | |
| 348 | + | } | |
| 349 | + | *floatsN++ | |
| 350 | + | } else { | |
| 351 | + | if i == 0 { | |
| 352 | + | r1 = f[numOfFloatRegisters()+*intsN] | |
| 353 | + | } else { | |
| 354 | + | r2 = f[numOfFloatRegisters()+*intsN] | |
| 355 | + | } | |
| 356 | + | *intsN++ | |
| 357 | + | } | |
| 358 | + | } | |
| 359 | + | ||
| 360 | + | if numEightbytes == 1 { | |
| 361 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| 362 | + | } | |
| 363 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 364 | + | } | |
| 365 | + | ||
| 288 | 366 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 289 | 367 | outSize := ret.Type().Size() | |
| 290 | 368 | switch { | |
| @@ -6,6 +6,7 @@ package purego | |||||
| 6 | import ( | 6 | import ( | ||
| 7 | "math" | 7 | "math" | ||
| 8 | "reflect" | 8 | "reflect" | ||
| 9 | + | "runtime" | |||
| 9 | "unsafe" | 10 | "unsafe" | ||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| @@ -285,6 +286,83 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 285 | panic("purego: bundleStackArgs should not be called on amd64") | 286 | panic("purego: bundleStackArgs should not be called on amd64") | ||
| 286 | } | 287 | } | ||
| 287 | 288 | ||||
| 289 | + | // getCallbackStruct reads a struct argument from the callback frame on amd64. | |||
| 290 | + | // It mirrors the SysV AMD64 ABI rules used by addStruct for the Go→C path. | |||
| 291 | + | // | |||
| 292 | + | // getCallbackStruct is only used on Unix. On Windows, callbacks are handled by | |||
| 293 | + | // the runtime's own callback mechanism, so this function is compiled but unused. | |||
| 294 | + | // | |||
| 295 | + | // SysV AMD64 struct argument passing rules: | |||
| 296 | + | // - Struct > 16 bytes (postMerger): passed on the stack as raw bytes | |||
| 297 | + | // - Struct ≤ 16 bytes: classify each eightbyte (INTEGER or SSE), | |||
| 298 | + | // read from the appropriate register class | |||
| 299 | + | // - If not enough registers for all eightbytes: entire struct goes on the stack | |||
| 300 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 301 | + | switch runtime.GOOS { | |||
| 302 | + | case "darwin", "freebsd", "linux", "netbsd": | |||
| 303 | + | default: | |||
| 304 | + | panic("purego: getCallbackStruct is not supported on " + runtime.GOOS) | |||
| 305 | + | } | |||
| 306 | + | ||||
| 307 | + | f := (*[callbackMaxFrame]uintptr)(frame) | |||
| 308 | + | size := inType.Size() | |||
| 309 | + | ||||
| 310 | + | // Structs > 16 bytes are passed on the stack as raw bytes (SysV ABI MEMORY class). | |||
| 311 | + | if postMerger(inType) { | |||
| 312 | + | numSlots := int((size + 7) / 8) | |||
| 313 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |||
| 314 | + | *stackSlot += numSlots | |||
| 315 | + | return v | |||
| 316 | + | } | |||
| 317 | + | ||||
| 318 | + | // Struct ≤ 16 bytes: classify each eightbyte and read from the appropriate register. | |||
| 319 | + | numEightbytes := int((size + 7) / 8) | |||
| 320 | + | ||||
| 321 | + | // Count how many integer and SSE registers this struct needs. | |||
| 322 | + | var needInts, needFloats int | |||
| 323 | + | for i := 0; i < numEightbytes; i++ { | |||
| 324 | + | class := classifyEightbyte(inType, uintptr(i)*8, uintptr(i)*8+8) | |||
| 325 | + | if class == _SSE { | |||
| 326 | + | needFloats++ | |||
| 327 | + | } else { | |||
| 328 | + | needInts++ | |||
| 329 | + | } | |||
| 330 | + | } | |||
| 331 | + | ||||
| 332 | + | // If not enough registers for all eightbytes, the entire struct goes on the stack. | |||
| 333 | + | if *intsN+needInts > numOfIntegerRegisters() || *floatsN+needFloats > numOfFloatRegisters() { | |||
| 334 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |||
| 335 | + | *stackSlot += numEightbytes | |||
| 336 | + | return v | |||
| 337 | + | } | |||
| 338 | + | ||||
| 339 | + | // Read each eightbyte from its appropriate register class. | |||
| 340 | + | var r1, r2 uintptr | |||
| 341 | + | for i := 0; i < numEightbytes; i++ { | |||
| 342 | + | class := classifyEightbyte(inType, uintptr(i)*8, uintptr(i)*8+8) | |||
| 343 | + | if class == _SSE { | |||
| 344 | + | if i == 0 { | |||
| 345 | + | r1 = f[*floatsN] | |||
| 346 | + | } else { | |||
| 347 | + | r2 = f[*floatsN] | |||
| 348 | + | } | |||
| 349 | + | *floatsN++ | |||
| 350 | + | } else { | |||
| 351 | + | if i == 0 { | |||
| 352 | + | r1 = f[numOfFloatRegisters()+*intsN] | |||
| 353 | + | } else { | |||
| 354 | + | r2 = f[numOfFloatRegisters()+*intsN] | |||
| 355 | + | } | |||
| 356 | + | *intsN++ | |||
| 357 | + | } | |||
| 358 | + | } | |||
| 359 | + | ||||
| 360 | + | if numEightbytes == 1 { | |||
| 361 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |||
| 362 | + | } | |||
| 363 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 364 | + | } | |||
| 365 | + | ||||
| 288 | func setStruct(a *callbackArgs, ret reflect.Value) { | 366 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 289 | outSize := ret.Type().Size() | 367 | outSize := ret.Type().Size() | ||
| 290 | switch { | 368 | switch { | ||
M struct_arm.go +4 -0
| @@ -84,6 +84,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 84 | 84 | panic("purego: bundleStackArgs should not be called on arm") | |
| 85 | 85 | } | |
| 86 | 86 | ||
| 87 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 88 | + | panic("purego: struct callback arguments are not supported on arm") | |
| 89 | + | } | |
| 90 | + | ||
| 87 | 91 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 88 | 92 | panic("purego: struct returns are not supported on arm") | |
| 89 | 93 | } | |
| @@ -84,6 +84,10 @@ 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 | 86 | ||||
| 87 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 88 | + | panic("purego: struct callback arguments are not supported on arm") | |||
| 89 | + | } | |||
| 90 | + | ||||
| 87 | func setStruct(a *callbackArgs, ret reflect.Value) { | 91 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 88 | panic("purego: struct returns are not supported on arm") | 92 | panic("purego: struct returns are not supported on arm") | ||
| 89 | } | 93 | } | ||
M struct_arm64.go +152 -0
| @@ -548,6 +548,158 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 548 | 548 | copyStruct8ByteChunks(ptr, size, addStack) | |
| 549 | 549 | } | |
| 550 | 550 | ||
| 551 | + | // getCallbackStruct reads a struct argument from the callback frame on arm64. | |
| 552 | + | // It mirrors the AAPCS64 rules used by addStruct for the Go→C path. | |
| 553 | + | // | |
| 554 | + | // getCallbackStruct is only used on Unix. On Windows, callbacks are handled by | |
| 555 | + | // the runtime's own callback mechanism, so this function is compiled but unused. | |
| 556 | + | // | |
| 557 | + | // AAPCS64 struct argument passing rules: | |
| 558 | + | // - HFA (Homogeneous Float Aggregate): each float field in a separate float register | |
| 559 | + | // - Non-HFA ≤ 16 bytes: packed into 1–2 integer registers | |
| 560 | + | // - > 16 bytes (not HFA): passed by pointer in integer register | |
| 561 | + | // - Register overflow: struct goes on the stack | |
| 562 | + | // - Darwin ARM64: byte-level packing on the stack | |
| 563 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 564 | + | switch runtime.GOOS { | |
| 565 | + | case "darwin", "freebsd", "linux", "netbsd": | |
| 566 | + | default: | |
| 567 | + | panic("purego: getCallbackStruct is not supported on " + runtime.GOOS) | |
| 568 | + | } | |
| 569 | + | ||
| 570 | + | f := (*[callbackMaxFrame]uintptr)(frame) | |
| 571 | + | ||
| 572 | + | if isHFA(inType) { | |
| 573 | + | _, numFloatFields := isAllSameFloat(inType) | |
| 574 | + | if *floatsN+numFloatFields <= numOfFloatRegisters() { | |
| 575 | + | return readHFAFromRegisters(inType, f, floatsN, numFloatFields) | |
| 576 | + | } | |
| 577 | + | // Not enough float registers: entire struct goes on stack. | |
| 578 | + | // Exhaust float registers per AAPCS64. | |
| 579 | + | *floatsN = numOfFloatRegisters() | |
| 580 | + | return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset) | |
| 581 | + | } | |
| 582 | + | ||
| 583 | + | if size := inType.Size(); size <= 16 { | |
| 584 | + | // Non-HFA composite ≤ 16 bytes: packed into 1–2 integer registers. | |
| 585 | + | numSlots := int((size + 7) / 8) | |
| 586 | + | if *intsN+numSlots <= numOfIntegerRegisters() { | |
| 587 | + | pos := numOfFloatRegisters() + *intsN | |
| 588 | + | var r1, r2 uintptr | |
| 589 | + | r1 = f[pos] | |
| 590 | + | *intsN++ | |
| 591 | + | if numSlots == 2 { | |
| 592 | + | r2 = f[pos+1] | |
| 593 | + | *intsN++ | |
| 594 | + | } | |
| 595 | + | if numSlots == 1 { | |
| 596 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| 597 | + | } | |
| 598 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 599 | + | } | |
| 600 | + | // Not enough int registers: goes on stack. | |
| 601 | + | return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset) | |
| 602 | + | } | |
| 603 | + | ||
| 604 | + | // > 16 bytes, not HFA: passed by pointer in integer register. | |
| 605 | + | if *intsN < numOfIntegerRegisters() { | |
| 606 | + | ptr := f[numOfFloatRegisters()+*intsN] | |
| 607 | + | *intsN++ | |
| 608 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |
| 609 | + | } | |
| 610 | + | ||
| 611 | + | // Pointer on stack (rare: all integer registers exhausted). | |
| 612 | + | if runtime.GOOS == "darwin" { | |
| 613 | + | ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0))) | |
| 614 | + | ptr := uintptr(ptrVal.Uint()) | |
| 615 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |
| 616 | + | } | |
| 617 | + | ptr := f[*stackSlot] | |
| 618 | + | *stackSlot++ | |
| 619 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |
| 620 | + | } | |
| 621 | + | ||
| 622 | + | // readHFAFromRegisters reads an HFA (Homogeneous Float Aggregate) struct from | |
| 623 | + | // float registers. Each float field occupies one float register slot. | |
| 624 | + | // For float32, the value is in the low 32 bits of the register. | |
| 625 | + | func readHFAFromRegisters(inType reflect.Type, f *[callbackMaxFrame]uintptr, floatsN *int, numFields int) reflect.Value { | |
| 626 | + | size := inType.Size() | |
| 627 | + | ||
| 628 | + | // Determine the element type | |
| 629 | + | root := inType.Field(0).Type | |
| 630 | + | for root.Kind() == reflect.Struct { | |
| 631 | + | root = root.Field(0).Type | |
| 632 | + | } | |
| 633 | + | isFloat32 := root.Kind() == reflect.Float32 | |
| 634 | + | ||
| 635 | + | if isFloat32 { | |
| 636 | + | // Each float32 is in a separate register (low 32 bits). | |
| 637 | + | // Pack pairs into uintptrs to match the struct memory layout. | |
| 638 | + | switch { | |
| 639 | + | case size <= 8: | |
| 640 | + | var r1 uintptr | |
| 641 | + | switch numFields { | |
| 642 | + | case 1: | |
| 643 | + | r1 = f[*floatsN] | |
| 644 | + | case 2: | |
| 645 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |
| 646 | + | } | |
| 647 | + | *floatsN += numFields | |
| 648 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| 649 | + | case size <= 16: | |
| 650 | + | var r1, r2 uintptr | |
| 651 | + | switch numFields { | |
| 652 | + | case 3: | |
| 653 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |
| 654 | + | r2 = f[*floatsN+2] | |
| 655 | + | case 4: | |
| 656 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |
| 657 | + | r2 = f[*floatsN+3]<<32 | f[*floatsN+2]&math.MaxUint32 | |
| 658 | + | } | |
| 659 | + | *floatsN += numFields | |
| 660 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 661 | + | default: | |
| 662 | + | panic("not reached") | |
| 663 | + | } | |
| 664 | + | } | |
| 665 | + | ||
| 666 | + | // float64: each field occupies one full register. | |
| 667 | + | switch numFields { | |
| 668 | + | case 1: | |
| 669 | + | r1 := f[*floatsN] | |
| 670 | + | *floatsN++ | |
| 671 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| 672 | + | case 2: | |
| 673 | + | r1, r2 := f[*floatsN], f[*floatsN+1] | |
| 674 | + | *floatsN += 2 | |
| 675 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 676 | + | case 3: | |
| 677 | + | r1, r2, r3 := f[*floatsN], f[*floatsN+1], f[*floatsN+2] | |
| 678 | + | *floatsN += 3 | |
| 679 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c uintptr }{r1, r2, r3})).Elem() | |
| 680 | + | case 4: | |
| 681 | + | r1, r2, r3, r4 := f[*floatsN], f[*floatsN+1], f[*floatsN+2], f[*floatsN+3] | |
| 682 | + | *floatsN += 4 | |
| 683 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{r1, r2, r3, r4})).Elem() | |
| 684 | + | default: | |
| 685 | + | panic("purego: HFA with more than 4 fields is not supported") | |
| 686 | + | } | |
| 687 | + | } | |
| 688 | + | ||
| 689 | + | // readStructFromStackArm64 reads a struct argument from the stack area of the | |
| 690 | + | // callback frame. On Darwin ARM64, arguments are byte-packed on the stack. | |
| 691 | + | // On Linux ARM64, arguments are 8-byte aligned. | |
| 692 | + | func readStructFromStackArm64(inType reflect.Type, f *[callbackMaxFrame]uintptr, frame unsafe.Pointer, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 693 | + | if runtime.GOOS == "darwin" { | |
| 694 | + | return callbackArgFromStack(frame, *stackSlot, stackByteOffset, inType) | |
| 695 | + | } | |
| 696 | + | // Linux ARM64: 8-byte aligned slots. | |
| 697 | + | numSlots := int((inType.Size() + 7) / 8) | |
| 698 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |
| 699 | + | *stackSlot += numSlots | |
| 700 | + | return v | |
| 701 | + | } | |
| 702 | + | ||
| 551 | 703 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 552 | 704 | outSize := ret.Type().Size() | |
| 553 | 705 | switch { | |
| @@ -548,6 +548,158 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 548 | copyStruct8ByteChunks(ptr, size, addStack) | 548 | copyStruct8ByteChunks(ptr, size, addStack) | ||
| 549 | } | 549 | } | ||
| 550 | 550 | ||||
| 551 | + | // getCallbackStruct reads a struct argument from the callback frame on arm64. | |||
| 552 | + | // It mirrors the AAPCS64 rules used by addStruct for the Go→C path. | |||
| 553 | + | // | |||
| 554 | + | // getCallbackStruct is only used on Unix. On Windows, callbacks are handled by | |||
| 555 | + | // the runtime's own callback mechanism, so this function is compiled but unused. | |||
| 556 | + | // | |||
| 557 | + | // AAPCS64 struct argument passing rules: | |||
| 558 | + | // - HFA (Homogeneous Float Aggregate): each float field in a separate float register | |||
| 559 | + | // - Non-HFA ≤ 16 bytes: packed into 1–2 integer registers | |||
| 560 | + | // - > 16 bytes (not HFA): passed by pointer in integer register | |||
| 561 | + | // - Register overflow: struct goes on the stack | |||
| 562 | + | // - Darwin ARM64: byte-level packing on the stack | |||
| 563 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 564 | + | switch runtime.GOOS { | |||
| 565 | + | case "darwin", "freebsd", "linux", "netbsd": | |||
| 566 | + | default: | |||
| 567 | + | panic("purego: getCallbackStruct is not supported on " + runtime.GOOS) | |||
| 568 | + | } | |||
| 569 | + | ||||
| 570 | + | f := (*[callbackMaxFrame]uintptr)(frame) | |||
| 571 | + | ||||
| 572 | + | if isHFA(inType) { | |||
| 573 | + | _, numFloatFields := isAllSameFloat(inType) | |||
| 574 | + | if *floatsN+numFloatFields <= numOfFloatRegisters() { | |||
| 575 | + | return readHFAFromRegisters(inType, f, floatsN, numFloatFields) | |||
| 576 | + | } | |||
| 577 | + | // Not enough float registers: entire struct goes on stack. | |||
| 578 | + | // Exhaust float registers per AAPCS64. | |||
| 579 | + | *floatsN = numOfFloatRegisters() | |||
| 580 | + | return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset) | |||
| 581 | + | } | |||
| 582 | + | ||||
| 583 | + | if size := inType.Size(); size <= 16 { | |||
| 584 | + | // Non-HFA composite ≤ 16 bytes: packed into 1–2 integer registers. | |||
| 585 | + | numSlots := int((size + 7) / 8) | |||
| 586 | + | if *intsN+numSlots <= numOfIntegerRegisters() { | |||
| 587 | + | pos := numOfFloatRegisters() + *intsN | |||
| 588 | + | var r1, r2 uintptr | |||
| 589 | + | r1 = f[pos] | |||
| 590 | + | *intsN++ | |||
| 591 | + | if numSlots == 2 { | |||
| 592 | + | r2 = f[pos+1] | |||
| 593 | + | *intsN++ | |||
| 594 | + | } | |||
| 595 | + | if numSlots == 1 { | |||
| 596 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |||
| 597 | + | } | |||
| 598 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 599 | + | } | |||
| 600 | + | // Not enough int registers: goes on stack. | |||
| 601 | + | return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset) | |||
| 602 | + | } | |||
| 603 | + | ||||
| 604 | + | // > 16 bytes, not HFA: passed by pointer in integer register. | |||
| 605 | + | if *intsN < numOfIntegerRegisters() { | |||
| 606 | + | ptr := f[numOfFloatRegisters()+*intsN] | |||
| 607 | + | *intsN++ | |||
| 608 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |||
| 609 | + | } | |||
| 610 | + | ||||
| 611 | + | // Pointer on stack (rare: all integer registers exhausted). | |||
| 612 | + | if runtime.GOOS == "darwin" { | |||
| 613 | + | ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0))) | |||
| 614 | + | ptr := uintptr(ptrVal.Uint()) | |||
| 615 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |||
| 616 | + | } | |||
| 617 | + | ptr := f[*stackSlot] | |||
| 618 | + | *stackSlot++ | |||
| 619 | + | return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem() | |||
| 620 | + | } | |||
| 621 | + | ||||
| 622 | + | // readHFAFromRegisters reads an HFA (Homogeneous Float Aggregate) struct from | |||
| 623 | + | // float registers. Each float field occupies one float register slot. | |||
| 624 | + | // For float32, the value is in the low 32 bits of the register. | |||
| 625 | + | func readHFAFromRegisters(inType reflect.Type, f *[callbackMaxFrame]uintptr, floatsN *int, numFields int) reflect.Value { | |||
| 626 | + | size := inType.Size() | |||
| 627 | + | ||||
| 628 | + | // Determine the element type | |||
| 629 | + | root := inType.Field(0).Type | |||
| 630 | + | for root.Kind() == reflect.Struct { | |||
| 631 | + | root = root.Field(0).Type | |||
| 632 | + | } | |||
| 633 | + | isFloat32 := root.Kind() == reflect.Float32 | |||
| 634 | + | ||||
| 635 | + | if isFloat32 { | |||
| 636 | + | // Each float32 is in a separate register (low 32 bits). | |||
| 637 | + | // Pack pairs into uintptrs to match the struct memory layout. | |||
| 638 | + | switch { | |||
| 639 | + | case size <= 8: | |||
| 640 | + | var r1 uintptr | |||
| 641 | + | switch numFields { | |||
| 642 | + | case 1: | |||
| 643 | + | r1 = f[*floatsN] | |||
| 644 | + | case 2: | |||
| 645 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |||
| 646 | + | } | |||
| 647 | + | *floatsN += numFields | |||
| 648 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |||
| 649 | + | case size <= 16: | |||
| 650 | + | var r1, r2 uintptr | |||
| 651 | + | switch numFields { | |||
| 652 | + | case 3: | |||
| 653 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |||
| 654 | + | r2 = f[*floatsN+2] | |||
| 655 | + | case 4: | |||
| 656 | + | r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32 | |||
| 657 | + | r2 = f[*floatsN+3]<<32 | f[*floatsN+2]&math.MaxUint32 | |||
| 658 | + | } | |||
| 659 | + | *floatsN += numFields | |||
| 660 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 661 | + | default: | |||
| 662 | + | panic("not reached") | |||
| 663 | + | } | |||
| 664 | + | } | |||
| 665 | + | ||||
| 666 | + | // float64: each field occupies one full register. | |||
| 667 | + | switch numFields { | |||
| 668 | + | case 1: | |||
| 669 | + | r1 := f[*floatsN] | |||
| 670 | + | *floatsN++ | |||
| 671 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |||
| 672 | + | case 2: | |||
| 673 | + | r1, r2 := f[*floatsN], f[*floatsN+1] | |||
| 674 | + | *floatsN += 2 | |||
| 675 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 676 | + | case 3: | |||
| 677 | + | r1, r2, r3 := f[*floatsN], f[*floatsN+1], f[*floatsN+2] | |||
| 678 | + | *floatsN += 3 | |||
| 679 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c uintptr }{r1, r2, r3})).Elem() | |||
| 680 | + | case 4: | |||
| 681 | + | r1, r2, r3, r4 := f[*floatsN], f[*floatsN+1], f[*floatsN+2], f[*floatsN+3] | |||
| 682 | + | *floatsN += 4 | |||
| 683 | + | return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{r1, r2, r3, r4})).Elem() | |||
| 684 | + | default: | |||
| 685 | + | panic("purego: HFA with more than 4 fields is not supported") | |||
| 686 | + | } | |||
| 687 | + | } | |||
| 688 | + | ||||
| 689 | + | // readStructFromStackArm64 reads a struct argument from the stack area of the | |||
| 690 | + | // callback frame. On Darwin ARM64, arguments are byte-packed on the stack. | |||
| 691 | + | // On Linux ARM64, arguments are 8-byte aligned. | |||
| 692 | + | func readStructFromStackArm64(inType reflect.Type, f *[callbackMaxFrame]uintptr, frame unsafe.Pointer, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 693 | + | if runtime.GOOS == "darwin" { | |||
| 694 | + | return callbackArgFromStack(frame, *stackSlot, stackByteOffset, inType) | |||
| 695 | + | } | |||
| 696 | + | // Linux ARM64: 8-byte aligned slots. | |||
| 697 | + | numSlots := int((inType.Size() + 7) / 8) | |||
| 698 | + | v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem() | |||
| 699 | + | *stackSlot += numSlots | |||
| 700 | + | return v | |||
| 701 | + | } | |||
| 702 | + | ||||
| 551 | func setStruct(a *callbackArgs, ret reflect.Value) { | 703 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 552 | outSize := ret.Type().Size() | 704 | outSize := ret.Type().Size() | ||
| 553 | switch { | 705 | switch { | ||
M struct_loong64.go +4 -0
| @@ -212,6 +212,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 212 | 212 | panic("purego: bundleStackArgs should not be called on loong64") | |
| 213 | 213 | } | |
| 214 | 214 | ||
| 215 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 216 | + | panic("purego: struct callback arguments are not supported on loong64") | |
| 217 | + | } | |
| 218 | + | ||
| 215 | 219 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 216 | 220 | panic("purego: struct returns are not supported on loong64") | |
| 217 | 221 | } | |
| @@ -212,6 +212,10 @@ 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 | 214 | ||||
| 215 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 216 | + | panic("purego: struct callback arguments are not supported on loong64") | |||
| 217 | + | } | |||
| 218 | + | ||||
| 215 | func setStruct(a *callbackArgs, ret reflect.Value) { | 219 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 216 | panic("purego: struct returns are not supported on loong64") | 220 | panic("purego: struct returns are not supported on loong64") | ||
| 217 | } | 221 | } | ||
M struct_ppc64le.go +4 -0
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 142 | 142 | panic("purego: bundleStackArgs should not be called on ppc64le") | |
| 143 | 143 | } | |
| 144 | 144 | ||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 146 | + | panic("purego: struct callback arguments are not supported on ppc64le") | |
| 147 | + | } | |
| 148 | + | ||
| 145 | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | 150 | panic("purego: struct returns are not supported on ppc64le") | |
| 147 | 151 | } | |
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 142 | panic("purego: bundleStackArgs should not be called on ppc64le") | 142 | panic("purego: bundleStackArgs should not be called on ppc64le") | ||
| 143 | } | 143 | } | ||
| 144 | 144 | ||||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 146 | + | panic("purego: struct callback arguments are not supported on ppc64le") | |||
| 147 | + | } | |||
| 148 | + | ||||
| 145 | func setStruct(a *callbackArgs, ret reflect.Value) { | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 146 | panic("purego: struct returns are not supported on ppc64le") | 150 | panic("purego: struct returns are not supported on ppc64le") | ||
| 147 | } | 151 | } | ||
M struct_riscv64.go +4 -0
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 142 | 142 | panic("purego: bundleStackArgs should not be called on riscv64") | |
| 143 | 143 | } | |
| 144 | 144 | ||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 146 | + | panic("purego: struct callback arguments are not supported on riscv64") | |
| 147 | + | } | |
| 148 | + | ||
| 145 | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | 150 | panic("purego: struct returns are not supported on riscv64") | |
| 147 | 151 | } | |
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 142 | panic("purego: bundleStackArgs should not be called on riscv64") | 142 | panic("purego: bundleStackArgs should not be called on riscv64") | ||
| 143 | } | 143 | } | ||
| 144 | 144 | ||||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 146 | + | panic("purego: struct callback arguments are not supported on riscv64") | |||
| 147 | + | } | |||
| 148 | + | ||||
| 145 | func setStruct(a *callbackArgs, ret reflect.Value) { | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 146 | panic("purego: struct returns are not supported on riscv64") | 150 | panic("purego: struct returns are not supported on riscv64") | ||
| 147 | } | 151 | } | ||
M struct_s390x.go +4 -0
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||
| 142 | 142 | panic("purego: bundleStackArgs should not be called on s390x") | |
| 143 | 143 | } | |
| 144 | 144 | ||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |
| 146 | + | panic("purego: struct callback arguments are not supported on s390x") | |
| 147 | + | } | |
| 148 | + | ||
| 145 | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | |
| 146 | 150 | panic("purego: struct returns are not supported on s390x") | |
| 147 | 151 | } | |
| @@ -142,6 +142,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) { | |||||
| 142 | panic("purego: bundleStackArgs should not be called on s390x") | 142 | panic("purego: bundleStackArgs should not be called on s390x") | ||
| 143 | } | 143 | } | ||
| 144 | 144 | ||||
| 145 | + | func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value { | |||
| 146 | + | panic("purego: struct callback arguments are not supported on s390x") | |||
| 147 | + | } | |||
| 148 | + | ||||
| 145 | func setStruct(a *callbackArgs, ret reflect.Value) { | 149 | func setStruct(a *callbackArgs, ret reflect.Value) { | ||
| 146 | panic("purego: struct returns are not supported on s390x") | 150 | panic("purego: struct returns are not supported on s390x") | ||
| 147 | } | 151 | } | ||
M struct_test.go +772 -482
| @@ -40,496 +40,784 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||
| 40 | 40 | expectedDouble float64 = 10 | |
| 41 | 41 | ) | |
| 42 | 42 | ||
| 43 | - | { | |
| 44 | - | type Empty struct{} | |
| 45 | - | var NoStruct func(Empty) int64 | |
| 46 | - | purego.RegisterLibFunc(&NoStruct, lib, "NoStruct") | |
| 47 | - | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | |
| 48 | - | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | |
| 49 | - | } | |
| 50 | - | } | |
| 51 | - | { | |
| 52 | - | type EmptyEmpty struct{} | |
| 53 | - | var EmptyEmptyFn func(EmptyEmpty) int64 | |
| 54 | - | purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty") | |
| 55 | - | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { | |
| 56 | - | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) | |
| 57 | - | } | |
| 58 | - | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 | |
| 59 | - | purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg") | |
| 60 | - | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { | |
| 61 | - | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) | |
| 62 | - | } | |
| 63 | - | } | |
| 64 | - | { | |
| 65 | - | type GreaterThan16Bytes struct { | |
| 66 | - | x, y, z *int64 | |
| 67 | - | } | |
| 68 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 69 | - | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 | |
| 70 | - | purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes") | |
| 71 | - | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 72 | - | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) | |
| 73 | - | } | |
| 43 | + | implementations := []struct { | |
| 44 | + | name string | |
| 45 | + | register func(fptr any, handle uintptr, name string, goFn any) | |
| 46 | + | }{ | |
| 47 | + | { | |
| 48 | + | name: "RegisterLibFunc", | |
| 49 | + | register: func(fptr any, handle uintptr, name string, _ any) { | |
| 50 | + | purego.RegisterLibFunc(fptr, handle, name) | |
| 51 | + | }, | |
| 52 | + | }, | |
| 53 | + | { | |
| 54 | + | name: "GoCallbackFunc", | |
| 55 | + | register: func(fptr any, handle uintptr, name string, goFn any) { | |
| 56 | + | fnType := reflect.TypeOf(fptr).Elem() | |
| 57 | + | if fnType.NumOut() > 0 { | |
| 58 | + | kind := fnType.Out(0).Kind() | |
| 59 | + | if kind == reflect.Float32 || kind == reflect.Float64 { | |
| 60 | + | // NewCallback doesn't support float returns. | |
| 61 | + | // Float struct args are covered by identity tests that return structs. | |
| 62 | + | purego.RegisterLibFunc(fptr, handle, name) | |
| 63 | + | return | |
| 64 | + | } | |
| 65 | + | } | |
| 66 | + | purego.RegisterFunc(fptr, purego.NewCallback(goFn)) | |
| 67 | + | }, | |
| 68 | + | }, | |
| 74 | 69 | } | |
| 75 | - | { | |
| 76 | - | type GreaterThan16BytesStruct struct { | |
| 77 | - | a struct { | |
| 78 | - | x, y, z *int64 | |
| 70 | + | for _, imp := range implementations { | |
| 71 | + | imp := imp | |
| 72 | + | t.Run(imp.name, func(t *testing.T) { | |
| 73 | + | register := imp.register | |
| 74 | + | { | |
| 75 | + | type Empty struct{} | |
| 76 | + | var NoStruct func(Empty) int64 | |
| 77 | + | register(&NoStruct, lib, "NoStruct", func(Empty) int64 { | |
| 78 | + | return 0xdeadbeef | |
| 79 | + | }) | |
| 80 | + | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | |
| 81 | + | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | |
| 82 | + | } | |
| 79 | 83 | } | |
| 80 | - | } | |
| 81 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 82 | - | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | |
| 83 | - | purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct") | |
| 84 | - | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | |
| 85 | - | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 86 | - | } | |
| 87 | - | } | |
| 88 | - | { | |
| 89 | - | type GreaterThan16Bytes struct { | |
| 90 | - | x, y, z *int64 | |
| 91 | - | } | |
| 92 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 93 | - | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | |
| 94 | - | purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters") | |
| 95 | - | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 96 | - | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 97 | - | } | |
| 98 | - | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |
| 99 | - | z -= 0xFF | |
| 100 | - | purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters") | |
| 101 | - | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |
| 102 | - | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 103 | - | } | |
| 104 | - | } | |
| 105 | - | { | |
| 106 | - | type IntLessThan16Bytes struct { | |
| 107 | - | x, y int64 | |
| 108 | - | } | |
| 109 | - | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |
| 110 | - | purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes") | |
| 111 | - | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |
| 112 | - | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 113 | - | } | |
| 114 | - | } | |
| 115 | - | { | |
| 116 | - | type FloatLessThan16Bytes struct { | |
| 117 | - | x, y float32 | |
| 118 | - | } | |
| 119 | - | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 | |
| 120 | - | purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes") | |
| 121 | - | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { | |
| 122 | - | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) | |
| 123 | - | } | |
| 124 | - | } | |
| 125 | - | { | |
| 126 | - | type ThreeSmallFields struct { | |
| 127 | - | x, y, z float32 | |
| 128 | - | } | |
| 129 | - | var ThreeSmallFieldsFn func(ThreeSmallFields) float32 | |
| 130 | - | purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields") | |
| 131 | - | if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat { | |
| 132 | - | t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat) | |
| 133 | - | } | |
| 134 | - | } | |
| 135 | - | { | |
| 136 | - | type FloatAndInt struct { | |
| 137 | - | x float32 | |
| 138 | - | y int32 | |
| 139 | - | } | |
| 140 | - | var FloatAndIntFn func(FloatAndInt) float32 | |
| 141 | - | purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt") | |
| 142 | - | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { | |
| 143 | - | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) | |
| 144 | - | } | |
| 145 | - | } | |
| 146 | - | { | |
| 147 | - | type DoubleStruct struct { | |
| 148 | - | x float64 | |
| 149 | - | } | |
| 150 | - | var DoubleStructFn func(DoubleStruct) float64 | |
| 151 | - | purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct") | |
| 152 | - | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { | |
| 153 | - | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 154 | - | } | |
| 155 | - | } | |
| 156 | - | { | |
| 157 | - | type TwoDoubleStruct struct { | |
| 158 | - | x, y float64 | |
| 159 | - | } | |
| 160 | - | var TwoDoubleStructFn func(TwoDoubleStruct) float64 | |
| 161 | - | purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct") | |
| 162 | - | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { | |
| 163 | - | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 164 | - | } | |
| 165 | - | } | |
| 166 | - | { | |
| 167 | - | type TwoDoubleTwoStruct struct { | |
| 168 | - | x struct { | |
| 169 | - | x, y float64 | |
| 84 | + | { | |
| 85 | + | type EmptyEmpty struct{} | |
| 86 | + | var EmptyEmptyFn func(EmptyEmpty) int64 | |
| 87 | + | register(&EmptyEmptyFn, lib, "EmptyEmpty", func(EmptyEmpty) int64 { | |
| 88 | + | return 0xdeadbeef | |
| 89 | + | }) | |
| 90 | + | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { | |
| 91 | + | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) | |
| 92 | + | } | |
| 93 | + | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 | |
| 94 | + | register(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg", func(x uint32, _ EmptyEmpty, y uint32) int64 { | |
| 95 | + | return int64(x)<<16 | int64(y) | |
| 96 | + | }) | |
| 97 | + | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { | |
| 98 | + | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) | |
| 99 | + | } | |
| 170 | 100 | } | |
| 171 | - | } | |
| 172 | - | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 | |
| 173 | - | purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct") | |
| 174 | - | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { | |
| 175 | - | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) | |
| 176 | - | } | |
| 177 | - | } | |
| 178 | - | { | |
| 179 | - | type ThreeDoubleStruct struct { | |
| 180 | - | x, y, z float64 | |
| 181 | - | } | |
| 182 | - | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 | |
| 183 | - | purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct") | |
| 184 | - | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { | |
| 185 | - | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) | |
| 186 | - | } | |
| 187 | - | } | |
| 188 | - | { | |
| 189 | - | type LargeFloatStruct struct { | |
| 190 | - | a, b, c, d, e, f float64 | |
| 191 | - | } | |
| 192 | - | var LargeFloatStructFn func(LargeFloatStruct) float64 | |
| 193 | - | purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct") | |
| 194 | - | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 195 | - | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) | |
| 196 | - | } | |
| 197 | - | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 | |
| 198 | - | purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs") | |
| 199 | - | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 200 | - | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) | |
| 201 | - | } | |
| 202 | - | } | |
| 203 | - | { | |
| 204 | - | type Rect struct { | |
| 205 | - | x, y, w, h float64 | |
| 206 | - | } | |
| 207 | - | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 | |
| 208 | - | purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs") | |
| 209 | - | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { | |
| 210 | - | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) | |
| 211 | - | } | |
| 212 | - | var RectangleSubtract func(rect Rect) float64 | |
| 213 | - | purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract") | |
| 214 | - | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { | |
| 215 | - | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) | |
| 216 | - | } | |
| 217 | - | var Rectangle func(rect Rect) float64 | |
| 218 | - | purego.RegisterLibFunc(&Rectangle, lib, "Rectangle") | |
| 219 | - | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { | |
| 220 | - | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) | |
| 221 | - | } | |
| 222 | - | } | |
| 223 | - | { | |
| 224 | - | type FloatArray struct { | |
| 225 | - | a [2]float64 | |
| 226 | - | } | |
| 227 | - | var FloatArrayFn func(rect FloatArray) float64 | |
| 228 | - | purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray") | |
| 229 | - | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { | |
| 230 | - | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) | |
| 231 | - | } | |
| 232 | - | } | |
| 233 | - | { | |
| 234 | - | type UnsignedChar4Bytes struct { | |
| 235 | - | a, b, c, d byte | |
| 236 | - | } | |
| 237 | - | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 | |
| 238 | - | purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes") | |
| 239 | - | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { | |
| 240 | - | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 241 | - | } | |
| 242 | - | } | |
| 243 | - | { | |
| 244 | - | type UnsignedChar4BytesStruct struct { | |
| 245 | - | x struct { | |
| 246 | - | a byte | |
| 101 | + | { | |
| 102 | + | type GreaterThan16Bytes struct { | |
| 103 | + | x, y, z *int64 | |
| 104 | + | } | |
| 105 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 106 | + | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 | |
| 107 | + | register(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes", func(g GreaterThan16Bytes) int64 { | |
| 108 | + | return *g.x + *g.y + *g.z | |
| 109 | + | }) | |
| 110 | + | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 111 | + | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) | |
| 112 | + | } | |
| 247 | 113 | } | |
| 248 | - | y struct { | |
| 249 | - | b byte | |
| 114 | + | { | |
| 115 | + | type GreaterThan16BytesStruct struct { | |
| 116 | + | a struct { | |
| 117 | + | x, y, z *int64 | |
| 118 | + | } | |
| 119 | + | } | |
| 120 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 121 | + | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | |
| 122 | + | register(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct", func(g GreaterThan16BytesStruct) int64 { | |
| 123 | + | return *g.a.x + *g.a.y + *g.a.z | |
| 124 | + | }) | |
| 125 | + | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | |
| 126 | + | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 127 | + | } | |
| 250 | 128 | } | |
| 251 | - | z struct { | |
| 252 | - | c byte | |
| 129 | + | { | |
| 130 | + | type GreaterThan16Bytes struct { | |
| 131 | + | x, y, z *int64 | |
| 132 | + | } | |
| 133 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 134 | + | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | |
| 135 | + | register(&AfterRegisters, lib, "AfterRegisters", func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 { | |
| 136 | + | registers := int64(a + b + c + d + e + f + g + h) | |
| 137 | + | stack := *bytes.x + *bytes.y + *bytes.z | |
| 138 | + | if registers != stack { | |
| 139 | + | return 0xbadbad | |
| 140 | + | } | |
| 141 | + | if stack != 0xdeadbeef { | |
| 142 | + | return 0xcafebad | |
| 143 | + | } | |
| 144 | + | return stack | |
| 145 | + | }) | |
| 146 | + | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 147 | + | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 148 | + | } | |
| 149 | + | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |
| 150 | + | z -= 0xFF | |
| 151 | + | register(&BeforeRegisters, lib, "BeforeRegisters", func(bytes GreaterThan16Bytes, a, b int64) uint64 { | |
| 152 | + | return uint64(*bytes.x + *bytes.y + *bytes.z + a + b) | |
| 153 | + | }) | |
| 154 | + | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |
| 155 | + | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 156 | + | } | |
| 253 | 157 | } | |
| 254 | - | w struct { | |
| 255 | - | d byte | |
| 158 | + | { | |
| 159 | + | type IntLessThan16Bytes struct { | |
| 160 | + | x, y int64 | |
| 161 | + | } | |
| 162 | + | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |
| 163 | + | register(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes", func(l IntLessThan16Bytes) int64 { | |
| 164 | + | return l.x + l.y | |
| 165 | + | }) | |
| 166 | + | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |
| 167 | + | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 168 | + | } | |
| 256 | 169 | } | |
| 257 | - | } | |
| 258 | - | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 | |
| 259 | - | purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct") | |
| 260 | - | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ | |
| 261 | - | x: struct{ a byte }{a: 0xDE}, | |
| 262 | - | y: struct{ b byte }{b: 0xAD}, | |
| 263 | - | z: struct{ c byte }{c: 0xBE}, | |
| 264 | - | w: struct{ d byte }{d: 0xEF}, | |
| 265 | - | }); ret != expectedUnsigned { | |
| 266 | - | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 267 | - | } | |
| 268 | - | } | |
| 269 | - | { | |
| 270 | - | type Short struct { | |
| 271 | - | a, b, c, d uint16 | |
| 272 | - | } | |
| 273 | - | var ShortFn func(Short) uint64 | |
| 274 | - | purego.RegisterLibFunc(&ShortFn, lib, "Short") | |
| 275 | - | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { | |
| 276 | - | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) | |
| 277 | - | } | |
| 278 | - | } | |
| 279 | - | { | |
| 280 | - | type Int struct { | |
| 281 | - | a, b uint32 | |
| 282 | - | } | |
| 283 | - | var IntFn func(Int) uint64 | |
| 284 | - | purego.RegisterLibFunc(&IntFn, lib, "Int") | |
| 285 | - | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { | |
| 286 | - | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) | |
| 287 | - | } | |
| 288 | - | } | |
| 289 | - | { | |
| 290 | - | type Long struct { | |
| 291 | - | a uint64 | |
| 292 | - | } | |
| 293 | - | var LongFn func(Long) uint64 | |
| 294 | - | purego.RegisterLibFunc(&LongFn, lib, "Long") | |
| 295 | - | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { | |
| 296 | - | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) | |
| 297 | - | } | |
| 298 | - | } | |
| 299 | - | { | |
| 300 | - | type Char8Bytes struct { | |
| 301 | - | a, b, c, d, e, f, g, h int8 | |
| 302 | - | } | |
| 303 | - | var Char8BytesFn func(Char8Bytes) int32 | |
| 304 | - | purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes") | |
| 305 | - | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { | |
| 306 | - | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) | |
| 307 | - | } | |
| 308 | - | } | |
| 309 | - | { | |
| 310 | - | type Odd struct { | |
| 311 | - | a, b, c byte | |
| 312 | - | } | |
| 313 | - | var OddFn func(Odd) int32 | |
| 314 | - | purego.RegisterLibFunc(&OddFn, lib, "Odd") | |
| 315 | - | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 316 | - | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) | |
| 317 | - | } | |
| 318 | - | } | |
| 319 | - | { | |
| 320 | - | type Char2Short1 struct { | |
| 321 | - | a, b byte | |
| 322 | - | c uint16 | |
| 323 | - | } | |
| 324 | - | var Char2Short1s func(Char2Short1) int32 | |
| 325 | - | purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s") | |
| 326 | - | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 327 | - | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | |
| 328 | - | } | |
| 329 | - | } | |
| 330 | - | { | |
| 331 | - | type SignedChar2Short1 struct { | |
| 332 | - | a, b int8 | |
| 333 | - | c int16 | |
| 334 | - | } | |
| 335 | - | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | |
| 336 | - | purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1") | |
| 337 | - | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | |
| 338 | - | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | |
| 339 | - | } | |
| 340 | - | } | |
| 341 | - | { | |
| 342 | - | type Array4UnsignedChars struct { | |
| 343 | - | a [4]uint8 | |
| 344 | - | } | |
| 345 | - | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | |
| 346 | - | purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars") | |
| 347 | - | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | |
| 348 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 349 | - | } | |
| 350 | - | } | |
| 351 | - | { | |
| 352 | - | type Array3UnsignedChar struct { | |
| 353 | - | a [3]uint8 | |
| 354 | - | } | |
| 355 | - | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | |
| 356 | - | purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars") | |
| 357 | - | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | |
| 358 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 359 | - | } | |
| 360 | - | } | |
| 361 | - | { | |
| 362 | - | type Array2UnsignedShort struct { | |
| 363 | - | a [2]uint16 | |
| 364 | - | } | |
| 365 | - | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | |
| 366 | - | purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts") | |
| 367 | - | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | |
| 368 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 369 | - | } | |
| 370 | - | } | |
| 371 | - | { | |
| 372 | - | type Array4Chars struct { | |
| 373 | - | a [4]int8 | |
| 374 | - | } | |
| 375 | - | var Array4CharsFn func(chars Array4Chars) int32 | |
| 376 | - | purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars") | |
| 377 | - | const expectedSum = 10 + 20 + 30 + 63 | |
| 378 | - | if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum { | |
| 379 | - | t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum) | |
| 380 | - | } | |
| 381 | - | } | |
| 382 | - | { | |
| 383 | - | type Array2Short struct { | |
| 384 | - | a [2]int16 | |
| 385 | - | } | |
| 386 | - | var Array2Shorts func(chars Array2Short) int32 | |
| 387 | - | purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts") | |
| 388 | - | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | |
| 389 | - | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 390 | - | } | |
| 391 | - | } | |
| 392 | - | { | |
| 393 | - | type Array3Short struct { | |
| 394 | - | a [3]int16 | |
| 395 | - | } | |
| 396 | - | var Array3Shorts func(chars Array3Short) int32 | |
| 397 | - | purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts") | |
| 398 | - | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | |
| 399 | - | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 400 | - | } | |
| 401 | - | } | |
| 402 | - | { | |
| 403 | - | type BoolStruct struct { | |
| 404 | - | b bool | |
| 405 | - | } | |
| 406 | - | var BoolStructFn func(BoolStruct) bool | |
| 407 | - | purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct") | |
| 408 | - | if ret := BoolStructFn(BoolStruct{true}); ret != true { | |
| 409 | - | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | |
| 410 | - | } | |
| 411 | - | if ret := BoolStructFn(BoolStruct{false}); ret != false { | |
| 412 | - | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | |
| 413 | - | } | |
| 414 | - | } | |
| 415 | - | { | |
| 416 | - | type BoolFloat struct { | |
| 417 | - | b bool | |
| 418 | - | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | |
| 419 | - | f float32 | |
| 420 | - | } | |
| 421 | - | var BoolFloatFn func(BoolFloat) float32 | |
| 422 | - | purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat") | |
| 423 | - | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | |
| 424 | - | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | |
| 425 | - | } | |
| 426 | - | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | |
| 427 | - | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | |
| 428 | - | } | |
| 429 | - | } | |
| 430 | - | { | |
| 431 | - | type point struct{ x, y float64 } | |
| 432 | - | type size struct{ width, height float64 } | |
| 433 | - | type Content struct { | |
| 434 | - | point point | |
| 435 | - | size size | |
| 436 | - | } | |
| 437 | - | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | |
| 438 | - | purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect") | |
| 439 | - | if ret := InitWithContentRect(new(int), | |
| 440 | - | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | |
| 441 | - | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | |
| 442 | - | 15, 4, true); ret != expectedUnsigned { | |
| 443 | - | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | |
| 444 | - | } | |
| 445 | - | } | |
| 446 | - | { | |
| 447 | - | type GoInt4 struct { | |
| 448 | - | A, B, C, D int | |
| 449 | - | } | |
| 450 | - | var GoInt4Fn func(GoInt4) int | |
| 451 | - | purego.RegisterLibFunc(&GoInt4Fn, lib, "GoInt4") | |
| 452 | - | const expected = math.MaxInt - 52 - 3 + 4 | |
| 453 | - | if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected { | |
| 454 | - | t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected) | |
| 455 | - | } | |
| 456 | - | } | |
| 457 | - | { | |
| 458 | - | type GoUint4 struct { | |
| 459 | - | A, B, C, D uint | |
| 460 | - | } | |
| 461 | - | var GoUint4Fn func(GoUint4) uint | |
| 462 | - | purego.RegisterLibFunc(&GoUint4Fn, lib, "GoUint4") | |
| 463 | - | const expected = 1_000_000 + 53 + 71 + 8 | |
| 464 | - | if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected { | |
| 465 | - | t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected) | |
| 466 | - | } | |
| 467 | - | } | |
| 468 | - | { | |
| 469 | - | type OneLong struct{ a uintptr } | |
| 470 | - | var TakeGoUintAndReturn func(a OneLong) uint64 | |
| 471 | - | purego.RegisterLibFunc(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn") | |
| 472 | - | expected := uint64(7) | |
| 473 | - | if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected { | |
| 474 | - | t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected) | |
| 475 | - | } | |
| 476 | - | } | |
| 477 | - | { | |
| 478 | - | type FloatAndBool struct { | |
| 479 | - | x float32 | |
| 480 | - | y bool | |
| 481 | - | } | |
| 482 | - | var FloatAndBoolFn func(FloatAndBool) int32 | |
| 483 | - | purego.RegisterLibFunc(&FloatAndBoolFn, lib, "FloatAndBool") | |
| 484 | - | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 { | |
| 485 | - | t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret) | |
| 486 | - | } | |
| 487 | - | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 { | |
| 488 | - | t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret) | |
| 489 | - | } | |
| 490 | - | } | |
| 491 | - | { | |
| 492 | - | type FourInt32s struct { | |
| 493 | - | f0, f1, f2, f3 int32 | |
| 494 | - | } | |
| 495 | - | var FourInt32sFn func(FourInt32s) int32 | |
| 496 | - | purego.RegisterLibFunc(&FourInt32sFn, lib, "FourInt32s") | |
| 497 | - | result := FourInt32sFn(FourInt32s{100, -127, 4, -100}) | |
| 498 | - | const want = 100 - 127 + 4 - 100 | |
| 499 | - | if result != want { | |
| 500 | - | t.Fatalf("FourInt32s returned %d wanted %d", result, want) | |
| 501 | - | } | |
| 502 | - | } | |
| 503 | - | { | |
| 504 | - | type PointerWrapper struct { | |
| 505 | - | ctx unsafe.Pointer | |
| 506 | - | } | |
| 507 | - | var ExtractPointer func(wrapper PointerWrapper) uintptr | |
| 508 | - | purego.RegisterLibFunc(&ExtractPointer, lib, "ExtractPointer") | |
| 170 | + | { | |
| 171 | + | type FloatLessThan16Bytes struct { | |
| 172 | + | x, y float32 | |
| 173 | + | } | |
| 174 | + | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 | |
| 175 | + | register(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes", func(f FloatLessThan16Bytes) float32 { | |
| 176 | + | return f.x + f.y | |
| 177 | + | }) | |
| 178 | + | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { | |
| 179 | + | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) | |
| 180 | + | } | |
| 181 | + | } | |
| 182 | + | { | |
| 183 | + | type ThreeSmallFields struct { | |
| 184 | + | x, y, z float32 | |
| 185 | + | } | |
| 186 | + | var ThreeSmallFieldsFn func(ThreeSmallFields) float32 | |
| 187 | + | register(&ThreeSmallFieldsFn, lib, "ThreeSmallFields", func(f ThreeSmallFields) float32 { | |
| 188 | + | return f.x + f.y + f.z | |
| 189 | + | }) | |
| 190 | + | if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat { | |
| 191 | + | t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat) | |
| 192 | + | } | |
| 193 | + | } | |
| 194 | + | { | |
| 195 | + | type FloatAndInt struct { | |
| 196 | + | x float32 | |
| 197 | + | y int32 | |
| 198 | + | } | |
| 199 | + | var FloatAndIntFn func(FloatAndInt) float32 | |
| 200 | + | register(&FloatAndIntFn, lib, "FloatAndInt", func(f FloatAndInt) float32 { | |
| 201 | + | return f.x + float32(f.y) | |
| 202 | + | }) | |
| 203 | + | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { | |
| 204 | + | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) | |
| 205 | + | } | |
| 206 | + | } | |
| 207 | + | { | |
| 208 | + | type DoubleStruct struct { | |
| 209 | + | x float64 | |
| 210 | + | } | |
| 211 | + | var DoubleStructFn func(DoubleStruct) float64 | |
| 212 | + | register(&DoubleStructFn, lib, "DoubleStruct", func(d DoubleStruct) float64 { | |
| 213 | + | return d.x | |
| 214 | + | }) | |
| 215 | + | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { | |
| 216 | + | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 217 | + | } | |
| 218 | + | } | |
| 219 | + | { | |
| 220 | + | type TwoDoubleStruct struct { | |
| 221 | + | x, y float64 | |
| 222 | + | } | |
| 223 | + | var TwoDoubleStructFn func(TwoDoubleStruct) float64 | |
| 224 | + | register(&TwoDoubleStructFn, lib, "TwoDoubleStruct", func(d TwoDoubleStruct) float64 { | |
| 225 | + | return d.x + d.y | |
| 226 | + | }) | |
| 227 | + | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { | |
| 228 | + | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 229 | + | } | |
| 230 | + | } | |
| 231 | + | { | |
| 232 | + | type TwoDoubleTwoStruct struct { | |
| 233 | + | x struct { | |
| 234 | + | x, y float64 | |
| 235 | + | } | |
| 236 | + | } | |
| 237 | + | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 | |
| 238 | + | register(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct", func(d TwoDoubleTwoStruct) float64 { | |
| 239 | + | return d.x.x + d.x.y | |
| 240 | + | }) | |
| 241 | + | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { | |
| 242 | + | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) | |
| 243 | + | } | |
| 244 | + | } | |
| 245 | + | { | |
| 246 | + | type ThreeDoubleStruct struct { | |
| 247 | + | x, y, z float64 | |
| 248 | + | } | |
| 249 | + | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 | |
| 250 | + | register(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct", func(d ThreeDoubleStruct) float64 { | |
| 251 | + | return d.x + d.y + d.z | |
| 252 | + | }) | |
| 253 | + | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { | |
| 254 | + | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) | |
| 255 | + | } | |
| 256 | + | } | |
| 257 | + | { | |
| 258 | + | type LargeFloatStruct struct { | |
| 259 | + | a, b, c, d, e, f float64 | |
| 260 | + | } | |
| 261 | + | var LargeFloatStructFn func(LargeFloatStruct) float64 | |
| 262 | + | register(&LargeFloatStructFn, lib, "LargeFloatStruct", func(s LargeFloatStruct) float64 { | |
| 263 | + | return s.a + s.b + s.c + s.d + s.e + s.f | |
| 264 | + | }) | |
| 265 | + | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 266 | + | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) | |
| 267 | + | } | |
| 268 | + | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 | |
| 269 | + | register(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs", func(a, b, c float64, s LargeFloatStruct) float64 { | |
| 270 | + | return a + b + c + s.a + s.b + s.c + s.d + s.e + s.f | |
| 271 | + | }) | |
| 272 | + | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 273 | + | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) | |
| 274 | + | } | |
| 275 | + | } | |
| 276 | + | { | |
| 277 | + | type Rect struct { | |
| 278 | + | x, y, w, h float64 | |
| 279 | + | } | |
| 280 | + | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 | |
| 281 | + | register(&RectangleWithRegs, lib, "RectangleWithRegs", func(a, b, c, d, e float64, rect Rect) float64 { | |
| 282 | + | return a + b + c + d + e + rect.x + rect.y + rect.w + rect.h | |
| 283 | + | }) | |
| 284 | + | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { | |
| 285 | + | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) | |
| 286 | + | } | |
| 287 | + | var RectangleSubtract func(rect Rect) float64 | |
| 288 | + | register(&RectangleSubtract, lib, "RectangleSubtract", func(rect Rect) float64 { | |
| 289 | + | return (rect.x + rect.y) - (rect.w + rect.h) | |
| 290 | + | }) | |
| 291 | + | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { | |
| 292 | + | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) | |
| 293 | + | } | |
| 294 | + | var Rectangle func(rect Rect) float64 | |
| 295 | + | register(&Rectangle, lib, "Rectangle", func(rect Rect) float64 { | |
| 296 | + | return rect.x + rect.y + rect.w + rect.h | |
| 297 | + | }) | |
| 298 | + | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { | |
| 299 | + | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) | |
| 300 | + | } | |
| 301 | + | } | |
| 302 | + | { | |
| 303 | + | type FloatArray struct { | |
| 304 | + | a [2]float64 | |
| 305 | + | } | |
| 306 | + | var FloatArrayFn func(rect FloatArray) float64 | |
| 307 | + | register(&FloatArrayFn, lib, "FloatArray", func(f FloatArray) float64 { | |
| 308 | + | return f.a[0] + f.a[1] | |
| 309 | + | }) | |
| 310 | + | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { | |
| 311 | + | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) | |
| 312 | + | } | |
| 313 | + | } | |
| 314 | + | { | |
| 315 | + | type UnsignedChar4Bytes struct { | |
| 316 | + | a, b, c, d byte | |
| 317 | + | } | |
| 318 | + | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 | |
| 319 | + | register(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes", func(b UnsignedChar4Bytes) uint32 { | |
| 320 | + | return uint32(b.a)<<24 | uint32(b.b)<<16 | uint32(b.c)<<8 | uint32(b.d) | |
| 321 | + | }) | |
| 322 | + | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { | |
| 323 | + | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 324 | + | } | |
| 325 | + | } | |
| 326 | + | { | |
| 327 | + | type UnsignedChar4BytesStruct struct { | |
| 328 | + | x struct { | |
| 329 | + | a byte | |
| 330 | + | } | |
| 331 | + | y struct { | |
| 332 | + | b byte | |
| 333 | + | } | |
| 334 | + | z struct { | |
| 335 | + | c byte | |
| 336 | + | } | |
| 337 | + | w struct { | |
| 338 | + | d byte | |
| 339 | + | } | |
| 340 | + | } | |
| 341 | + | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 | |
| 342 | + | register(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct", func(b UnsignedChar4BytesStruct) uint32 { | |
| 343 | + | return uint32(b.x.a)<<24 | uint32(b.y.b)<<16 | uint32(b.z.c)<<8 | uint32(b.w.d) | |
| 344 | + | }) | |
| 345 | + | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ | |
| 346 | + | x: struct{ a byte }{a: 0xDE}, | |
| 347 | + | y: struct{ b byte }{b: 0xAD}, | |
| 348 | + | z: struct{ c byte }{c: 0xBE}, | |
| 349 | + | w: struct{ d byte }{d: 0xEF}, | |
| 350 | + | }); ret != expectedUnsigned { | |
| 351 | + | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 352 | + | } | |
| 353 | + | } | |
| 354 | + | { | |
| 355 | + | type Short struct { | |
| 356 | + | a, b, c, d uint16 | |
| 357 | + | } | |
| 358 | + | var ShortFn func(Short) uint64 | |
| 359 | + | register(&ShortFn, lib, "Short", func(s Short) uint64 { | |
| 360 | + | return uint64(s.a)<<48 | uint64(s.b)<<32 | uint64(s.c)<<16 | uint64(s.d) | |
| 361 | + | }) | |
| 362 | + | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { | |
| 363 | + | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) | |
| 364 | + | } | |
| 365 | + | } | |
| 366 | + | { | |
| 367 | + | type Int struct { | |
| 368 | + | a, b uint32 | |
| 369 | + | } | |
| 370 | + | var IntFn func(Int) uint64 | |
| 371 | + | register(&IntFn, lib, "Int", func(i Int) uint64 { | |
| 372 | + | return uint64(i.a)<<32 | uint64(i.b) | |
| 373 | + | }) | |
| 374 | + | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { | |
| 375 | + | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) | |
| 376 | + | } | |
| 377 | + | } | |
| 378 | + | { | |
| 379 | + | type Long struct { | |
| 380 | + | a uint64 | |
| 381 | + | } | |
| 382 | + | var LongFn func(Long) uint64 | |
| 383 | + | register(&LongFn, lib, "Long", func(l Long) uint64 { | |
| 384 | + | return l.a | |
| 385 | + | }) | |
| 386 | + | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { | |
| 387 | + | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) | |
| 388 | + | } | |
| 389 | + | } | |
| 390 | + | { | |
| 391 | + | type Char8Bytes struct { | |
| 392 | + | a, b, c, d, e, f, g, h int8 | |
| 393 | + | } | |
| 394 | + | var Char8BytesFn func(Char8Bytes) int32 | |
| 395 | + | register(&Char8BytesFn, lib, "Char8Bytes", func(b Char8Bytes) int32 { | |
| 396 | + | return int32(b.a) + int32(b.b) + int32(b.c) + int32(b.d) + int32(b.e) + int32(b.f) + int32(b.g) + int32(b.h) | |
| 397 | + | }) | |
| 398 | + | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { | |
| 399 | + | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) | |
| 400 | + | } | |
| 401 | + | } | |
| 402 | + | { | |
| 403 | + | type Odd struct { | |
| 404 | + | a, b, c byte | |
| 405 | + | } | |
| 406 | + | var OddFn func(Odd) int32 | |
| 407 | + | register(&OddFn, lib, "Odd", func(o Odd) int32 { | |
| 408 | + | return int32(o.a) + int32(o.b) + int32(o.c) | |
| 409 | + | }) | |
| 410 | + | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 411 | + | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) | |
| 412 | + | } | |
| 413 | + | } | |
| 414 | + | { | |
| 415 | + | type Char2Short1 struct { | |
| 416 | + | a, b byte | |
| 417 | + | c uint16 | |
| 418 | + | } | |
| 419 | + | var Char2Short1s func(Char2Short1) int32 | |
| 420 | + | register(&Char2Short1s, lib, "Char2Short1s", func(s Char2Short1) int32 { | |
| 421 | + | return int32(s.a) + int32(s.b) + int32(s.c) | |
| 422 | + | }) | |
| 423 | + | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 424 | + | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | |
| 425 | + | } | |
| 426 | + | } | |
| 427 | + | { | |
| 428 | + | type SignedChar2Short1 struct { | |
| 429 | + | a, b int8 | |
| 430 | + | c int16 | |
| 431 | + | } | |
| 432 | + | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | |
| 433 | + | register(&SignedChar2Short1Fn, lib, "SignedChar2Short1", func(s SignedChar2Short1) int32 { | |
| 434 | + | return int32(s.a) + int32(s.b) + int32(s.c) | |
| 435 | + | }) | |
| 436 | + | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | |
| 437 | + | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | |
| 438 | + | } | |
| 439 | + | } | |
| 440 | + | { | |
| 441 | + | type Array4UnsignedChars struct { | |
| 442 | + | a [4]uint8 | |
| 443 | + | } | |
| 444 | + | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | |
| 445 | + | register(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars", func(a Array4UnsignedChars) uint32 { | |
| 446 | + | return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | uint32(a.a[3]) | |
| 447 | + | }) | |
| 448 | + | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | |
| 449 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 450 | + | } | |
| 451 | + | } | |
| 452 | + | { | |
| 453 | + | type Array3UnsignedChar struct { | |
| 454 | + | a [3]uint8 | |
| 455 | + | } | |
| 456 | + | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | |
| 457 | + | register(&Array3UnsignedChars, lib, "Array3UnsignedChars", func(a Array3UnsignedChar) uint32 { | |
| 458 | + | return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | 0xef | |
| 459 | + | }) | |
| 460 | + | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | |
| 461 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 462 | + | } | |
| 463 | + | } | |
| 464 | + | { | |
| 465 | + | type Array2UnsignedShort struct { | |
| 466 | + | a [2]uint16 | |
| 467 | + | } | |
| 468 | + | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | |
| 469 | + | register(&Array2UnsignedShorts, lib, "Array2UnsignedShorts", func(a Array2UnsignedShort) uint32 { | |
| 470 | + | return uint32(a.a[0])<<16 | uint32(a.a[1]) | |
| 471 | + | }) | |
| 472 | + | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | |
| 473 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 474 | + | } | |
| 475 | + | } | |
| 476 | + | { | |
| 477 | + | type Array4Chars struct { | |
| 478 | + | a [4]int8 | |
| 479 | + | } | |
| 480 | + | var Array4CharsFn func(chars Array4Chars) int32 | |
| 481 | + | register(&Array4CharsFn, lib, "Array4Chars", func(a Array4Chars) int32 { | |
| 482 | + | return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2]) + int32(a.a[3]) | |
| 483 | + | }) | |
| 484 | + | const expectedSum = 10 + 20 + 30 + 63 | |
| 485 | + | if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum { | |
| 486 | + | t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum) | |
| 487 | + | } | |
| 488 | + | } | |
| 489 | + | { | |
| 490 | + | type Array2Short struct { | |
| 491 | + | a [2]int16 | |
| 492 | + | } | |
| 493 | + | var Array2Shorts func(chars Array2Short) int32 | |
| 494 | + | register(&Array2Shorts, lib, "Array2Shorts", func(a Array2Short) int32 { | |
| 495 | + | return int32(a.a[0]) + int32(a.a[1]) | |
| 496 | + | }) | |
| 497 | + | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | |
| 498 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 499 | + | } | |
| 500 | + | } | |
| 501 | + | { | |
| 502 | + | type Array3Short struct { | |
| 503 | + | a [3]int16 | |
| 504 | + | } | |
| 505 | + | var Array3Shorts func(chars Array3Short) int32 | |
| 506 | + | register(&Array3Shorts, lib, "Array3Shorts", func(a Array3Short) int32 { | |
| 507 | + | return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2]) | |
| 508 | + | }) | |
| 509 | + | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | |
| 510 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 511 | + | } | |
| 512 | + | } | |
| 513 | + | { | |
| 514 | + | type BoolStruct struct { | |
| 515 | + | b bool | |
| 516 | + | } | |
| 517 | + | var BoolStructFn func(BoolStruct) bool | |
| 518 | + | register(&BoolStructFn, lib, "BoolStruct", func(b BoolStruct) bool { | |
| 519 | + | return b.b | |
| 520 | + | }) | |
| 521 | + | if ret := BoolStructFn(BoolStruct{true}); ret != true { | |
| 522 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | |
| 523 | + | } | |
| 524 | + | if ret := BoolStructFn(BoolStruct{false}); ret != false { | |
| 525 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | |
| 526 | + | } | |
| 527 | + | } | |
| 528 | + | { | |
| 529 | + | type BoolFloat struct { | |
| 530 | + | b bool | |
| 531 | + | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | |
| 532 | + | f float32 | |
| 533 | + | } | |
| 534 | + | var BoolFloatFn func(BoolFloat) float32 | |
| 535 | + | register(&BoolFloatFn, lib, "BoolFloat", func(s BoolFloat) float32 { | |
| 536 | + | if s.b { | |
| 537 | + | return s.f | |
| 538 | + | } | |
| 539 | + | return -s.f | |
| 540 | + | }) | |
| 541 | + | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | |
| 542 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | |
| 543 | + | } | |
| 544 | + | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | |
| 545 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | |
| 546 | + | } | |
| 547 | + | } | |
| 548 | + | { | |
| 549 | + | type point struct{ x, y float64 } | |
| 550 | + | type size struct{ width, height float64 } | |
| 551 | + | type Content struct { | |
| 552 | + | point point | |
| 553 | + | size size | |
| 554 | + | } | |
| 555 | + | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | |
| 556 | + | register(&InitWithContentRect, lib, "InitWithContentRect", func(win *int, c Content, style, backing int32, flag bool) uint64 { | |
| 557 | + | if win == nil { | |
| 558 | + | return 0xBAD | |
| 559 | + | } | |
| 560 | + | if !flag { | |
| 561 | + | return 0xF1A6 | |
| 562 | + | } | |
| 563 | + | return uint64(c.point.x+c.point.y+c.size.width+c.size.height) / uint64(style-backing) | |
| 564 | + | }) | |
| 565 | + | if ret := InitWithContentRect(new(int), | |
| 566 | + | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | |
| 567 | + | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | |
| 568 | + | 15, 4, true); ret != expectedUnsigned { | |
| 569 | + | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | |
| 570 | + | } | |
| 571 | + | } | |
| 572 | + | { | |
| 573 | + | type GoInt4 struct { | |
| 574 | + | A, B, C, D int | |
| 575 | + | } | |
| 576 | + | var GoInt4Fn func(GoInt4) int | |
| 577 | + | register(&GoInt4Fn, lib, "GoInt4", func(g GoInt4) int { | |
| 578 | + | return g.A + g.B - g.C + g.D | |
| 579 | + | }) | |
| 580 | + | const expected = math.MaxInt - 52 - 3 + 4 | |
| 581 | + | if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected { | |
| 582 | + | t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected) | |
| 583 | + | } | |
| 584 | + | } | |
| 585 | + | { | |
| 586 | + | type GoUint4 struct { | |
| 587 | + | A, B, C, D uint | |
| 588 | + | } | |
| 589 | + | var GoUint4Fn func(GoUint4) uint | |
| 590 | + | register(&GoUint4Fn, lib, "GoUint4", func(g GoUint4) uint { | |
| 591 | + | return g.A + g.B + g.C + g.D | |
| 592 | + | }) | |
| 593 | + | const expected = 1_000_000 + 53 + 71 + 8 | |
| 594 | + | if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected { | |
| 595 | + | t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected) | |
| 596 | + | } | |
| 597 | + | } | |
| 598 | + | { | |
| 599 | + | type OneLong struct{ a uintptr } | |
| 600 | + | var TakeGoUintAndReturn func(a OneLong) uint64 | |
| 601 | + | register(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn", func(a OneLong) uint64 { | |
| 602 | + | return uint64(a.a) | |
| 603 | + | }) | |
| 604 | + | expected := uint64(7) | |
| 605 | + | if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected { | |
| 606 | + | t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected) | |
| 607 | + | } | |
| 608 | + | } | |
| 609 | + | { | |
| 610 | + | type FloatAndBool struct { | |
| 611 | + | x float32 | |
| 612 | + | y bool | |
| 613 | + | } | |
| 614 | + | var FloatAndBoolFn func(FloatAndBool) int32 | |
| 615 | + | register(&FloatAndBoolFn, lib, "FloatAndBool", func(f FloatAndBool) int32 { | |
| 616 | + | if f.y { | |
| 617 | + | return 1 | |
| 618 | + | } | |
| 619 | + | return 0 | |
| 620 | + | }) | |
| 621 | + | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 { | |
| 622 | + | t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret) | |
| 623 | + | } | |
| 624 | + | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 { | |
| 625 | + | t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret) | |
| 626 | + | } | |
| 627 | + | } | |
| 628 | + | { | |
| 629 | + | type FourInt32s struct { | |
| 630 | + | f0, f1, f2, f3 int32 | |
| 631 | + | } | |
| 632 | + | var FourInt32sFn func(FourInt32s) int32 | |
| 633 | + | register(&FourInt32sFn, lib, "FourInt32s", func(s FourInt32s) int32 { | |
| 634 | + | return s.f0 + s.f1 + s.f2 + s.f3 | |
| 635 | + | }) | |
| 636 | + | result := FourInt32sFn(FourInt32s{100, -127, 4, -100}) | |
| 637 | + | const want = 100 - 127 + 4 - 100 | |
| 638 | + | if result != want { | |
| 639 | + | t.Fatalf("FourInt32s returned %d wanted %d", result, want) | |
| 640 | + | } | |
| 641 | + | } | |
| 642 | + | { | |
| 643 | + | type PointerWrapper struct { | |
| 644 | + | ctx unsafe.Pointer | |
| 645 | + | } | |
| 646 | + | var ExtractPointer func(wrapper PointerWrapper) uintptr | |
| 647 | + | register(&ExtractPointer, lib, "ExtractPointer", func(wrapper PointerWrapper) uintptr { | |
| 648 | + | return uintptr(wrapper.ctx) | |
| 649 | + | }) | |
| 509 | 650 | ||
| 510 | - | var v int | |
| 511 | - | ptr := unsafe.Pointer(&v) | |
| 512 | - | expected := uintptr(ptr) | |
| 513 | - | result := ExtractPointer(PointerWrapper{ctx: ptr}) | |
| 514 | - | if result != expected { | |
| 515 | - | t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected) | |
| 516 | - | } | |
| 517 | - | } | |
| 518 | - | { | |
| 519 | - | type TwoPointers struct { | |
| 520 | - | ptr1, ptr2 unsafe.Pointer | |
| 521 | - | } | |
| 522 | - | var AddPointers func(wrapper TwoPointers) uintptr | |
| 523 | - | purego.RegisterLibFunc(&AddPointers, lib, "AddPointers") | |
| 651 | + | var v int | |
| 652 | + | ptr := unsafe.Pointer(&v) | |
| 653 | + | expected := uintptr(ptr) | |
| 654 | + | result := ExtractPointer(PointerWrapper{ctx: ptr}) | |
| 655 | + | if result != expected { | |
| 656 | + | t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected) | |
| 657 | + | } | |
| 658 | + | } | |
| 659 | + | { | |
| 660 | + | type TwoPointers struct { | |
| 661 | + | ptr1, ptr2 unsafe.Pointer | |
| 662 | + | } | |
| 663 | + | var AddPointers func(wrapper TwoPointers) uintptr | |
| 664 | + | register(&AddPointers, lib, "AddPointers", func(wrapper TwoPointers) uintptr { | |
| 665 | + | return uintptr(wrapper.ptr1) + uintptr(wrapper.ptr2) | |
| 666 | + | }) | |
| 524 | 667 | ||
| 525 | - | var v1, v2 int | |
| 526 | - | ptr1 := unsafe.Pointer(&v1) | |
| 527 | - | ptr2 := unsafe.Pointer(&v2) | |
| 528 | - | expected := uintptr(ptr1) + uintptr(ptr2) | |
| 529 | - | result := AddPointers(TwoPointers{ptr1, ptr2}) | |
| 530 | - | if result != expected { | |
| 531 | - | t.Fatalf("AddPointers returned %#x wanted %#x", result, expected) | |
| 532 | - | } | |
| 668 | + | var v1, v2 int | |
| 669 | + | ptr1 := unsafe.Pointer(&v1) | |
| 670 | + | ptr2 := unsafe.Pointer(&v2) | |
| 671 | + | expected := uintptr(ptr1) + uintptr(ptr2) | |
| 672 | + | result := AddPointers(TwoPointers{ptr1, ptr2}) | |
| 673 | + | if result != expected { | |
| 674 | + | t.Fatalf("AddPointers returned %#x wanted %#x", result, expected) | |
| 675 | + | } | |
| 676 | + | } | |
| 677 | + | ||
| 678 | + | // Identity struct arg tests | |
| 679 | + | { | |
| 680 | + | // Single int64: one integer register. | |
| 681 | + | type OneInt64 struct{ A int64 } | |
| 682 | + | var fn func(OneInt64) OneInt64 | |
| 683 | + | register(&fn, lib, "IdentityOneInt64", func(s OneInt64) OneInt64 { | |
| 684 | + | return s | |
| 685 | + | }) | |
| 686 | + | expected := OneInt64{0xDEADBEEF} | |
| 687 | + | if ret := fn(expected); ret != expected { | |
| 688 | + | t.Fatalf("IdentityOneInt64 returned %+v wanted %+v", ret, expected) | |
| 689 | + | } | |
| 690 | + | } | |
| 691 | + | { | |
| 692 | + | // Two int64 fields: two integer registers. | |
| 693 | + | type IntLessThan16Bytes struct{ X, Y int64 } | |
| 694 | + | var fn func(IntLessThan16Bytes) IntLessThan16Bytes | |
| 695 | + | register(&fn, lib, "IdentityIntLessThan16Bytes", func(s IntLessThan16Bytes) IntLessThan16Bytes { | |
| 696 | + | return s | |
| 697 | + | }) | |
| 698 | + | expected := IntLessThan16Bytes{0xDEADBEEF, -42} | |
| 699 | + | if ret := fn(expected); ret != expected { | |
| 700 | + | t.Fatalf("IdentityIntLessThan16Bytes returned %+v wanted %+v", ret, expected) | |
| 701 | + | } | |
| 702 | + | } | |
| 703 | + | { | |
| 704 | + | // Two float64 fields: SSE on amd64, HFA on arm64. | |
| 705 | + | type TwoDoubleStruct struct{ X, Y float64 } | |
| 706 | + | var fn func(TwoDoubleStruct) TwoDoubleStruct | |
| 707 | + | register(&fn, lib, "IdentityTwoDoubleStruct", func(s TwoDoubleStruct) TwoDoubleStruct { | |
| 708 | + | return s | |
| 709 | + | }) | |
| 710 | + | expected := TwoDoubleStruct{3.14, -2.71} | |
| 711 | + | if ret := fn(expected); ret != expected { | |
| 712 | + | t.Fatalf("IdentityTwoDoubleStruct returned %+v wanted %+v", ret, expected) | |
| 713 | + | } | |
| 714 | + | } | |
| 715 | + | { | |
| 716 | + | // Four float32 fields: HFA on arm64, SSE on amd64. | |
| 717 | + | type FourFloat32 struct{ A, B, C, D float32 } | |
| 718 | + | var fn func(FourFloat32) FourFloat32 | |
| 719 | + | register(&fn, lib, "IdentityFourFloat32", func(s FourFloat32) FourFloat32 { | |
| 720 | + | return s | |
| 721 | + | }) | |
| 722 | + | expected := FourFloat32{1.0, 2.0, 3.0, 4.0} | |
| 723 | + | if ret := fn(expected); ret != expected { | |
| 724 | + | t.Fatalf("IdentityFourFloat32 returned %+v wanted %+v", ret, expected) | |
| 725 | + | } | |
| 726 | + | } | |
| 727 | + | { | |
| 728 | + | // Mixed float32 + int32: INTEGER class eightbyte on amd64, non-HFA int regs on arm64. | |
| 729 | + | type FloatAndInt struct { | |
| 730 | + | X float32 | |
| 731 | + | Y int32 | |
| 732 | + | } | |
| 733 | + | var fn func(FloatAndInt) FloatAndInt | |
| 734 | + | register(&fn, lib, "IdentityFloatAndInt", func(s FloatAndInt) FloatAndInt { | |
| 735 | + | return s | |
| 736 | + | }) | |
| 737 | + | expected := FloatAndInt{7.5, 42} | |
| 738 | + | if ret := fn(expected); ret != expected { | |
| 739 | + | t.Fatalf("IdentityFloatAndInt returned %+v wanted %+v", ret, expected) | |
| 740 | + | } | |
| 741 | + | } | |
| 742 | + | { | |
| 743 | + | // Struct > 16 bytes: hidden pointer on amd64, pointer in int register on arm64. | |
| 744 | + | type ThreeInt64 struct{ A, B, C int64 } | |
| 745 | + | var fn func(ThreeInt64) ThreeInt64 | |
| 746 | + | register(&fn, lib, "IdentityThreeInt64", func(s ThreeInt64) ThreeInt64 { | |
| 747 | + | return s | |
| 748 | + | }) | |
| 749 | + | expected := ThreeInt64{1, 2, 3} | |
| 750 | + | if ret := fn(expected); ret != expected { | |
| 751 | + | t.Fatalf("IdentityThreeInt64 returned %+v wanted %+v", ret, expected) | |
| 752 | + | } | |
| 753 | + | } | |
| 754 | + | { | |
| 755 | + | // Struct > 16 bytes with pointer fields. | |
| 756 | + | type PtrInt64Ptr struct { | |
| 757 | + | A *int64 | |
| 758 | + | B int64 | |
| 759 | + | C *int64 | |
| 760 | + | } | |
| 761 | + | a, c := new(int64), new(int64) | |
| 762 | + | *a, *c = 100, 300 | |
| 763 | + | var fn func(PtrInt64Ptr) PtrInt64Ptr | |
| 764 | + | register(&fn, lib, "IdentityPtrInt64Ptr", func(s PtrInt64Ptr) PtrInt64Ptr { | |
| 765 | + | return s | |
| 766 | + | }) | |
| 767 | + | expected := PtrInt64Ptr{a, 200, c} | |
| 768 | + | ret := fn(expected) | |
| 769 | + | if *ret.A != *expected.A || ret.B != expected.B || *ret.C != *expected.C { | |
| 770 | + | t.Fatalf("IdentityPtrInt64Ptr returned %+v wanted %+v", ret, expected) | |
| 771 | + | } | |
| 772 | + | runtime.KeepAlive(a) | |
| 773 | + | runtime.KeepAlive(c) | |
| 774 | + | } | |
| 775 | + | { | |
| 776 | + | // Struct arg after some primitive args: register offset correctness. | |
| 777 | + | type IntLessThan16Bytes struct{ X, Y int64 } | |
| 778 | + | var fn func(int64, float64, IntLessThan16Bytes) IntLessThan16Bytes | |
| 779 | + | register(&fn, lib, "IdentityTwoInt64AfterPrims", func(x int64, y float64, s IntLessThan16Bytes) IntLessThan16Bytes { | |
| 780 | + | return s | |
| 781 | + | }) | |
| 782 | + | expected := IntLessThan16Bytes{100, 200} | |
| 783 | + | if ret := fn(1, 2.0, expected); ret != expected { | |
| 784 | + | t.Fatalf("IdentityTwoInt64AfterPrims returned %+v wanted %+v", ret, expected) | |
| 785 | + | } | |
| 786 | + | } | |
| 787 | + | { | |
| 788 | + | // Float struct arg after float primitive args: float register offset. | |
| 789 | + | type FloatLessThan16Bytes struct{ X, Y float32 } | |
| 790 | + | var fn func(float64, float64, FloatLessThan16Bytes) FloatLessThan16Bytes | |
| 791 | + | register(&fn, lib, "IdentityTwoFloat32AfterFloats", func(x, y float64, s FloatLessThan16Bytes) FloatLessThan16Bytes { | |
| 792 | + | return s | |
| 793 | + | }) | |
| 794 | + | expected := FloatLessThan16Bytes{5.0, 6.0} | |
| 795 | + | if ret := fn(1.0, 2.0, expected); ret != expected { | |
| 796 | + | t.Fatalf("IdentityTwoFloat32AfterFloats returned %+v wanted %+v", ret, expected) | |
| 797 | + | } | |
| 798 | + | } | |
| 799 | + | { | |
| 800 | + | // Mixed struct with pointer: pointer + int32 + float32 + int32. | |
| 801 | + | type Mixed5Args struct { | |
| 802 | + | A *int64 | |
| 803 | + | B int32 | |
| 804 | + | C float32 | |
| 805 | + | D int32 | |
| 806 | + | } | |
| 807 | + | ptr := new(int64) | |
| 808 | + | *ptr = 99 | |
| 809 | + | var fn func(Mixed5Args) Mixed5Args | |
| 810 | + | register(&fn, lib, "IdentityMixed5Args", func(s Mixed5Args) Mixed5Args { | |
| 811 | + | return s | |
| 812 | + | }) | |
| 813 | + | expected := Mixed5Args{ptr, 1, 7.2, 9} | |
| 814 | + | ret := fn(expected) | |
| 815 | + | if ret.A != expected.A || ret.B != expected.B || ret.C != expected.C || ret.D != expected.D { | |
| 816 | + | t.Fatalf("IdentityMixed5Args returned %+v wanted %+v", ret, expected) | |
| 817 | + | } | |
| 818 | + | runtime.KeepAlive(ptr) | |
| 819 | + | } | |
| 820 | + | }) | |
| 533 | 821 | } | |
| 534 | 822 | } | |
| 535 | 823 | ||
| @@ -889,7 +1177,9 @@ func TestRegisterFunc_structReturns(t *testing.T) { | |||
| 889 | 1177 | D int32 | |
| 890 | 1178 | } | |
| 891 | 1179 | var IdentityMixed5 func(m Mixed5) Mixed5 | |
| 892 | - | // TODO: when struct args are supported in NewCallback use register() instead | |
| 1180 | + | // The GoCallbackFunc register helper decomposes args into struct fields, | |
| 1181 | + | // which doesn't work for identity functions with struct args. | |
| 1182 | + | // Struct callback args are tested in TestRegisterFunc_structArgs. | |
| 893 | 1183 | purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5") | |
| 894 | 1184 | ptr := new(int64) | |
| 895 | 1185 | expected := Mixed5{ptr, 1, 7.2, 9} | |
| @@ -40,496 +40,784 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||||
| 40 | expectedDouble float64 = 10 | 40 | expectedDouble float64 = 10 | ||
| 41 | ) | 41 | ) | ||
| 42 | 42 | ||||
| 43 | - | { | 43 | + | implementations := []struct { |
| 44 | - | type Empty struct{} | 44 | + | name string |
| 45 | - | var NoStruct func(Empty) int64 | 45 | + | register func(fptr any, handle uintptr, name string, goFn any) |
| 46 | - | purego.RegisterLibFunc(&NoStruct, lib, "NoStruct") | 46 | + | }{ |
| 47 | - | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | 47 | + | { |
| 48 | - | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | 48 | + | name: "RegisterLibFunc", |
| 49 | - | } | 49 | + | register: func(fptr any, handle uintptr, name string, _ any) { |
| 50 | - | } | 50 | + | purego.RegisterLibFunc(fptr, handle, name) |
| 51 | - | { | 51 | + | }, |
| 52 | - | type EmptyEmpty struct{} | 52 | + | }, |
| 53 | - | var EmptyEmptyFn func(EmptyEmpty) int64 | 53 | + | { |
| 54 | - | purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty") | 54 | + | name: "GoCallbackFunc", |
| 55 | - | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { | 55 | + | register: func(fptr any, handle uintptr, name string, goFn any) { |
| 56 | - | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) | 56 | + | fnType := reflect.TypeOf(fptr).Elem() |
| 57 | - | } | 57 | + | if fnType.NumOut() > 0 { |
| 58 | - | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 | 58 | + | kind := fnType.Out(0).Kind() |
| 59 | - | purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg") | 59 | + | if kind == reflect.Float32 || kind == reflect.Float64 { |
| 60 | - | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { | 60 | + | // NewCallback doesn't support float returns. |
| 61 | - | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) | 61 | + | // Float struct args are covered by identity tests that return structs. |
| 62 | - | } | 62 | + | purego.RegisterLibFunc(fptr, handle, name) |
| 63 | - | } | 63 | + | return |
| 64 | - | { | 64 | + | } |
| 65 | - | type GreaterThan16Bytes struct { | 65 | + | } |
| 66 | - | x, y, z *int64 | 66 | + | purego.RegisterFunc(fptr, purego.NewCallback(goFn)) |
| 67 | - | } | 67 | + | }, |
| 68 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | 68 | + | }, |
| 69 | - | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 | |||
| 70 | - | purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes") | |||
| 71 | - | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |||
| 72 | - | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 73 | - | } | |||
| 74 | } | 69 | } | ||
| 75 | - | { | 70 | + | for _, imp := range implementations { |
| 76 | - | type GreaterThan16BytesStruct struct { | 71 | + | imp := imp |
| 77 | - | a struct { | 72 | + | t.Run(imp.name, func(t *testing.T) { |
| 78 | - | x, y, z *int64 | 73 | + | register := imp.register |
| 74 | + | { | |||
| 75 | + | type Empty struct{} | |||
| 76 | + | var NoStruct func(Empty) int64 | |||
| 77 | + | register(&NoStruct, lib, "NoStruct", func(Empty) int64 { | |||
| 78 | + | return 0xdeadbeef | |||
| 79 | + | }) | |||
| 80 | + | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | |||
| 81 | + | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 82 | + | } | |||
| 79 | } | 83 | } | ||
| 80 | - | } | 84 | + | { |
| 81 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | 85 | + | type EmptyEmpty struct{} |
| 82 | - | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | 86 | + | var EmptyEmptyFn func(EmptyEmpty) int64 |
| 83 | - | purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct") | 87 | + | register(&EmptyEmptyFn, lib, "EmptyEmpty", func(EmptyEmpty) int64 { |
| 84 | - | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | 88 | + | return 0xdeadbeef |
| 85 | - | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | 89 | + | }) |
| 86 | - | } | 90 | + | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { |
| 87 | - | } | 91 | + | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) |
| 88 | - | { | 92 | + | } |
| 89 | - | type GreaterThan16Bytes struct { | 93 | + | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 |
| 90 | - | x, y, z *int64 | 94 | + | register(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg", func(x uint32, _ EmptyEmpty, y uint32) int64 { |
| 91 | - | } | 95 | + | return int64(x)<<16 | int64(y) |
| 92 | - | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | 96 | + | }) |
| 93 | - | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | 97 | + | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { |
| 94 | - | purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters") | 98 | + | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) |
| 95 | - | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | 99 | + | } |
| 96 | - | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 97 | - | } | |||
| 98 | - | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |||
| 99 | - | z -= 0xFF | |||
| 100 | - | purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters") | |||
| 101 | - | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |||
| 102 | - | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 103 | - | } | |||
| 104 | - | } | |||
| 105 | - | { | |||
| 106 | - | type IntLessThan16Bytes struct { | |||
| 107 | - | x, y int64 | |||
| 108 | - | } | |||
| 109 | - | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |||
| 110 | - | purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes") | |||
| 111 | - | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |||
| 112 | - | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 113 | - | } | |||
| 114 | - | } | |||
| 115 | - | { | |||
| 116 | - | type FloatLessThan16Bytes struct { | |||
| 117 | - | x, y float32 | |||
| 118 | - | } | |||
| 119 | - | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 | |||
| 120 | - | purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes") | |||
| 121 | - | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { | |||
| 122 | - | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) | |||
| 123 | - | } | |||
| 124 | - | } | |||
| 125 | - | { | |||
| 126 | - | type ThreeSmallFields struct { | |||
| 127 | - | x, y, z float32 | |||
| 128 | - | } | |||
| 129 | - | var ThreeSmallFieldsFn func(ThreeSmallFields) float32 | |||
| 130 | - | purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields") | |||
| 131 | - | if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat { | |||
| 132 | - | t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat) | |||
| 133 | - | } | |||
| 134 | - | } | |||
| 135 | - | { | |||
| 136 | - | type FloatAndInt struct { | |||
| 137 | - | x float32 | |||
| 138 | - | y int32 | |||
| 139 | - | } | |||
| 140 | - | var FloatAndIntFn func(FloatAndInt) float32 | |||
| 141 | - | purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt") | |||
| 142 | - | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { | |||
| 143 | - | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) | |||
| 144 | - | } | |||
| 145 | - | } | |||
| 146 | - | { | |||
| 147 | - | type DoubleStruct struct { | |||
| 148 | - | x float64 | |||
| 149 | - | } | |||
| 150 | - | var DoubleStructFn func(DoubleStruct) float64 | |||
| 151 | - | purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct") | |||
| 152 | - | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { | |||
| 153 | - | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) | |||
| 154 | - | } | |||
| 155 | - | } | |||
| 156 | - | { | |||
| 157 | - | type TwoDoubleStruct struct { | |||
| 158 | - | x, y float64 | |||
| 159 | - | } | |||
| 160 | - | var TwoDoubleStructFn func(TwoDoubleStruct) float64 | |||
| 161 | - | purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct") | |||
| 162 | - | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { | |||
| 163 | - | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) | |||
| 164 | - | } | |||
| 165 | - | } | |||
| 166 | - | { | |||
| 167 | - | type TwoDoubleTwoStruct struct { | |||
| 168 | - | x struct { | |||
| 169 | - | x, y float64 | |||
| 170 | } | 100 | } | ||
| 171 | - | } | 101 | + | { |
| 172 | - | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 | 102 | + | type GreaterThan16Bytes struct { |
| 173 | - | purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct") | 103 | + | x, y, z *int64 |
| 174 | - | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { | 104 | + | } |
| 175 | - | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) | 105 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 |
| 176 | - | } | 106 | + | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 |
| 177 | - | } | 107 | + | register(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes", func(g GreaterThan16Bytes) int64 { |
| 178 | - | { | 108 | + | return *g.x + *g.y + *g.z |
| 179 | - | type ThreeDoubleStruct struct { | 109 | + | }) |
| 180 | - | x, y, z float64 | 110 | + | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { |
| 181 | - | } | 111 | + | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) |
| 182 | - | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 | 112 | + | } |
| 183 | - | purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct") | |||
| 184 | - | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { | |||
| 185 | - | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) | |||
| 186 | - | } | |||
| 187 | - | } | |||
| 188 | - | { | |||
| 189 | - | type LargeFloatStruct struct { | |||
| 190 | - | a, b, c, d, e, f float64 | |||
| 191 | - | } | |||
| 192 | - | var LargeFloatStructFn func(LargeFloatStruct) float64 | |||
| 193 | - | purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct") | |||
| 194 | - | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |||
| 195 | - | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) | |||
| 196 | - | } | |||
| 197 | - | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 | |||
| 198 | - | purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs") | |||
| 199 | - | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |||
| 200 | - | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) | |||
| 201 | - | } | |||
| 202 | - | } | |||
| 203 | - | { | |||
| 204 | - | type Rect struct { | |||
| 205 | - | x, y, w, h float64 | |||
| 206 | - | } | |||
| 207 | - | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 | |||
| 208 | - | purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs") | |||
| 209 | - | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { | |||
| 210 | - | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) | |||
| 211 | - | } | |||
| 212 | - | var RectangleSubtract func(rect Rect) float64 | |||
| 213 | - | purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract") | |||
| 214 | - | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { | |||
| 215 | - | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) | |||
| 216 | - | } | |||
| 217 | - | var Rectangle func(rect Rect) float64 | |||
| 218 | - | purego.RegisterLibFunc(&Rectangle, lib, "Rectangle") | |||
| 219 | - | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { | |||
| 220 | - | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) | |||
| 221 | - | } | |||
| 222 | - | } | |||
| 223 | - | { | |||
| 224 | - | type FloatArray struct { | |||
| 225 | - | a [2]float64 | |||
| 226 | - | } | |||
| 227 | - | var FloatArrayFn func(rect FloatArray) float64 | |||
| 228 | - | purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray") | |||
| 229 | - | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { | |||
| 230 | - | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) | |||
| 231 | - | } | |||
| 232 | - | } | |||
| 233 | - | { | |||
| 234 | - | type UnsignedChar4Bytes struct { | |||
| 235 | - | a, b, c, d byte | |||
| 236 | - | } | |||
| 237 | - | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 | |||
| 238 | - | purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes") | |||
| 239 | - | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { | |||
| 240 | - | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 241 | - | } | |||
| 242 | - | } | |||
| 243 | - | { | |||
| 244 | - | type UnsignedChar4BytesStruct struct { | |||
| 245 | - | x struct { | |||
| 246 | - | a byte | |||
| 247 | } | 113 | } | ||
| 248 | - | y struct { | 114 | + | { |
| 249 | - | b byte | 115 | + | type GreaterThan16BytesStruct struct { |
| 116 | + | a struct { | |||
| 117 | + | x, y, z *int64 | |||
| 118 | + | } | |||
| 119 | + | } | |||
| 120 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |||
| 121 | + | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | |||
| 122 | + | register(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct", func(g GreaterThan16BytesStruct) int64 { | |||
| 123 | + | return *g.a.x + *g.a.y + *g.a.z | |||
| 124 | + | }) | |||
| 125 | + | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | |||
| 126 | + | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 127 | + | } | |||
| 250 | } | 128 | } | ||
| 251 | - | z struct { | 129 | + | { |
| 252 | - | c byte | 130 | + | type GreaterThan16Bytes struct { |
| 131 | + | x, y, z *int64 | |||
| 132 | + | } | |||
| 133 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |||
| 134 | + | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | |||
| 135 | + | register(&AfterRegisters, lib, "AfterRegisters", func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 { | |||
| 136 | + | registers := int64(a + b + c + d + e + f + g + h) | |||
| 137 | + | stack := *bytes.x + *bytes.y + *bytes.z | |||
| 138 | + | if registers != stack { | |||
| 139 | + | return 0xbadbad | |||
| 140 | + | } | |||
| 141 | + | if stack != 0xdeadbeef { | |||
| 142 | + | return 0xcafebad | |||
| 143 | + | } | |||
| 144 | + | return stack | |||
| 145 | + | }) | |||
| 146 | + | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |||
| 147 | + | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 148 | + | } | |||
| 149 | + | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |||
| 150 | + | z -= 0xFF | |||
| 151 | + | register(&BeforeRegisters, lib, "BeforeRegisters", func(bytes GreaterThan16Bytes, a, b int64) uint64 { | |||
| 152 | + | return uint64(*bytes.x + *bytes.y + *bytes.z + a + b) | |||
| 153 | + | }) | |||
| 154 | + | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |||
| 155 | + | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 156 | + | } | |||
| 253 | } | 157 | } | ||
| 254 | - | w struct { | 158 | + | { |
| 255 | - | d byte | 159 | + | type IntLessThan16Bytes struct { |
| 160 | + | x, y int64 | |||
| 161 | + | } | |||
| 162 | + | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |||
| 163 | + | register(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes", func(l IntLessThan16Bytes) int64 { | |||
| 164 | + | return l.x + l.y | |||
| 165 | + | }) | |||
| 166 | + | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |||
| 167 | + | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 168 | + | } | |||
| 256 | } | 169 | } | ||
| 257 | - | } | 170 | + | { |
| 258 | - | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 | 171 | + | type FloatLessThan16Bytes struct { |
| 259 | - | purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct") | 172 | + | x, y float32 |
| 260 | - | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ | 173 | + | } |
| 261 | - | x: struct{ a byte }{a: 0xDE}, | 174 | + | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 |
| 262 | - | y: struct{ b byte }{b: 0xAD}, | 175 | + | register(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes", func(f FloatLessThan16Bytes) float32 { |
| 263 | - | z: struct{ c byte }{c: 0xBE}, | 176 | + | return f.x + f.y |
| 264 | - | w: struct{ d byte }{d: 0xEF}, | 177 | + | }) |
| 265 | - | }); ret != expectedUnsigned { | 178 | + | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { |
| 266 | - | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | 179 | + | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) |
| 267 | - | } | 180 | + | } |
| 268 | - | } | 181 | + | } |
| 269 | - | { | 182 | + | { |
| 270 | - | type Short struct { | 183 | + | type ThreeSmallFields struct { |
| 271 | - | a, b, c, d uint16 | 184 | + | x, y, z float32 |
| 272 | - | } | 185 | + | } |
| 273 | - | var ShortFn func(Short) uint64 | 186 | + | var ThreeSmallFieldsFn func(ThreeSmallFields) float32 |
| 274 | - | purego.RegisterLibFunc(&ShortFn, lib, "Short") | 187 | + | register(&ThreeSmallFieldsFn, lib, "ThreeSmallFields", func(f ThreeSmallFields) float32 { |
| 275 | - | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { | 188 | + | return f.x + f.y + f.z |
| 276 | - | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) | 189 | + | }) |
| 277 | - | } | 190 | + | if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat { |
| 278 | - | } | 191 | + | t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat) |
| 279 | - | { | 192 | + | } |
| 280 | - | type Int struct { | 193 | + | } |
| 281 | - | a, b uint32 | 194 | + | { |
| 282 | - | } | 195 | + | type FloatAndInt struct { |
| 283 | - | var IntFn func(Int) uint64 | 196 | + | x float32 |
| 284 | - | purego.RegisterLibFunc(&IntFn, lib, "Int") | 197 | + | y int32 |
| 285 | - | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { | 198 | + | } |
| 286 | - | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) | 199 | + | var FloatAndIntFn func(FloatAndInt) float32 |
| 287 | - | } | 200 | + | register(&FloatAndIntFn, lib, "FloatAndInt", func(f FloatAndInt) float32 { |
| 288 | - | } | 201 | + | return f.x + float32(f.y) |
| 289 | - | { | 202 | + | }) |
| 290 | - | type Long struct { | 203 | + | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { |
| 291 | - | a uint64 | 204 | + | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) |
| 292 | - | } | 205 | + | } |
| 293 | - | var LongFn func(Long) uint64 | 206 | + | } |
| 294 | - | purego.RegisterLibFunc(&LongFn, lib, "Long") | 207 | + | { |
| 295 | - | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { | 208 | + | type DoubleStruct struct { |
| 296 | - | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) | 209 | + | x float64 |
| 297 | - | } | 210 | + | } |
| 298 | - | } | 211 | + | var DoubleStructFn func(DoubleStruct) float64 |
| 299 | - | { | 212 | + | register(&DoubleStructFn, lib, "DoubleStruct", func(d DoubleStruct) float64 { |
| 300 | - | type Char8Bytes struct { | 213 | + | return d.x |
| 301 | - | a, b, c, d, e, f, g, h int8 | 214 | + | }) |
| 302 | - | } | 215 | + | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { |
| 303 | - | var Char8BytesFn func(Char8Bytes) int32 | 216 | + | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) |
| 304 | - | purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes") | 217 | + | } |
| 305 | - | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { | 218 | + | } |
| 306 | - | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) | 219 | + | { |
| 307 | - | } | 220 | + | type TwoDoubleStruct struct { |
| 308 | - | } | 221 | + | x, y float64 |
| 309 | - | { | 222 | + | } |
| 310 | - | type Odd struct { | 223 | + | var TwoDoubleStructFn func(TwoDoubleStruct) float64 |
| 311 | - | a, b, c byte | 224 | + | register(&TwoDoubleStructFn, lib, "TwoDoubleStruct", func(d TwoDoubleStruct) float64 { |
| 312 | - | } | 225 | + | return d.x + d.y |
| 313 | - | var OddFn func(Odd) int32 | 226 | + | }) |
| 314 | - | purego.RegisterLibFunc(&OddFn, lib, "Odd") | 227 | + | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { |
| 315 | - | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { | 228 | + | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) |
| 316 | - | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) | 229 | + | } |
| 317 | - | } | 230 | + | } |
| 318 | - | } | 231 | + | { |
| 319 | - | { | 232 | + | type TwoDoubleTwoStruct struct { |
| 320 | - | type Char2Short1 struct { | 233 | + | x struct { |
| 321 | - | a, b byte | 234 | + | x, y float64 |
| 322 | - | c uint16 | 235 | + | } |
| 323 | - | } | 236 | + | } |
| 324 | - | var Char2Short1s func(Char2Short1) int32 | 237 | + | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 |
| 325 | - | purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s") | 238 | + | register(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct", func(d TwoDoubleTwoStruct) float64 { |
| 326 | - | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | 239 | + | return d.x.x + d.x.y |
| 327 | - | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | 240 | + | }) |
| 328 | - | } | 241 | + | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { |
| 329 | - | } | 242 | + | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) |
| 330 | - | { | 243 | + | } |
| 331 | - | type SignedChar2Short1 struct { | 244 | + | } |
| 332 | - | a, b int8 | 245 | + | { |
| 333 | - | c int16 | 246 | + | type ThreeDoubleStruct struct { |
| 334 | - | } | 247 | + | x, y, z float64 |
| 335 | - | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | 248 | + | } |
| 336 | - | purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1") | 249 | + | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 |
| 337 | - | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | 250 | + | register(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct", func(d ThreeDoubleStruct) float64 { |
| 338 | - | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | 251 | + | return d.x + d.y + d.z |
| 339 | - | } | 252 | + | }) |
| 340 | - | } | 253 | + | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { |
| 341 | - | { | 254 | + | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) |
| 342 | - | type Array4UnsignedChars struct { | 255 | + | } |
| 343 | - | a [4]uint8 | 256 | + | } |
| 344 | - | } | 257 | + | { |
| 345 | - | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | 258 | + | type LargeFloatStruct struct { |
| 346 | - | purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars") | 259 | + | a, b, c, d, e, f float64 |
| 347 | - | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | 260 | + | } |
| 348 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | 261 | + | var LargeFloatStructFn func(LargeFloatStruct) float64 |
| 349 | - | } | 262 | + | register(&LargeFloatStructFn, lib, "LargeFloatStruct", func(s LargeFloatStruct) float64 { |
| 350 | - | } | 263 | + | return s.a + s.b + s.c + s.d + s.e + s.f |
| 351 | - | { | 264 | + | }) |
| 352 | - | type Array3UnsignedChar struct { | 265 | + | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { |
| 353 | - | a [3]uint8 | 266 | + | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) |
| 354 | - | } | 267 | + | } |
| 355 | - | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | 268 | + | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 |
| 356 | - | purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars") | 269 | + | register(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs", func(a, b, c float64, s LargeFloatStruct) float64 { |
| 357 | - | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | 270 | + | return a + b + c + s.a + s.b + s.c + s.d + s.e + s.f |
| 358 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | 271 | + | }) |
| 359 | - | } | 272 | + | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { |
| 360 | - | } | 273 | + | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) |
| 361 | - | { | 274 | + | } |
| 362 | - | type Array2UnsignedShort struct { | 275 | + | } |
| 363 | - | a [2]uint16 | 276 | + | { |
| 364 | - | } | 277 | + | type Rect struct { |
| 365 | - | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | 278 | + | x, y, w, h float64 |
| 366 | - | purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts") | 279 | + | } |
| 367 | - | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | 280 | + | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 |
| 368 | - | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | 281 | + | register(&RectangleWithRegs, lib, "RectangleWithRegs", func(a, b, c, d, e float64, rect Rect) float64 { |
| 369 | - | } | 282 | + | return a + b + c + d + e + rect.x + rect.y + rect.w + rect.h |
| 370 | - | } | 283 | + | }) |
| 371 | - | { | 284 | + | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { |
| 372 | - | type Array4Chars struct { | 285 | + | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) |
| 373 | - | a [4]int8 | 286 | + | } |
| 374 | - | } | 287 | + | var RectangleSubtract func(rect Rect) float64 |
| 375 | - | var Array4CharsFn func(chars Array4Chars) int32 | 288 | + | register(&RectangleSubtract, lib, "RectangleSubtract", func(rect Rect) float64 { |
| 376 | - | purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars") | 289 | + | return (rect.x + rect.y) - (rect.w + rect.h) |
| 377 | - | const expectedSum = 10 + 20 + 30 + 63 | 290 | + | }) |
| 378 | - | if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum { | 291 | + | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { |
| 379 | - | t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum) | 292 | + | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) |
| 380 | - | } | 293 | + | } |
| 381 | - | } | 294 | + | var Rectangle func(rect Rect) float64 |
| 382 | - | { | 295 | + | register(&Rectangle, lib, "Rectangle", func(rect Rect) float64 { |
| 383 | - | type Array2Short struct { | 296 | + | return rect.x + rect.y + rect.w + rect.h |
| 384 | - | a [2]int16 | 297 | + | }) |
| 385 | - | } | 298 | + | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { |
| 386 | - | var Array2Shorts func(chars Array2Short) int32 | 299 | + | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) |
| 387 | - | purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts") | 300 | + | } |
| 388 | - | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | 301 | + | } |
| 389 | - | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | 302 | + | { |
| 390 | - | } | 303 | + | type FloatArray struct { |
| 391 | - | } | 304 | + | a [2]float64 |
| 392 | - | { | 305 | + | } |
| 393 | - | type Array3Short struct { | 306 | + | var FloatArrayFn func(rect FloatArray) float64 |
| 394 | - | a [3]int16 | 307 | + | register(&FloatArrayFn, lib, "FloatArray", func(f FloatArray) float64 { |
| 395 | - | } | 308 | + | return f.a[0] + f.a[1] |
| 396 | - | var Array3Shorts func(chars Array3Short) int32 | 309 | + | }) |
| 397 | - | purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts") | 310 | + | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { |
| 398 | - | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | 311 | + | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) |
| 399 | - | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | 312 | + | } |
| 400 | - | } | 313 | + | } |
| 401 | - | } | 314 | + | { |
| 402 | - | { | 315 | + | type UnsignedChar4Bytes struct { |
| 403 | - | type BoolStruct struct { | 316 | + | a, b, c, d byte |
| 404 | - | b bool | 317 | + | } |
| 405 | - | } | 318 | + | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 |
| 406 | - | var BoolStructFn func(BoolStruct) bool | 319 | + | register(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes", func(b UnsignedChar4Bytes) uint32 { |
| 407 | - | purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct") | 320 | + | return uint32(b.a)<<24 | uint32(b.b)<<16 | uint32(b.c)<<8 | uint32(b.d) |
| 408 | - | if ret := BoolStructFn(BoolStruct{true}); ret != true { | 321 | + | }) |
| 409 | - | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | 322 | + | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { |
| 410 | - | } | 323 | + | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) |
| 411 | - | if ret := BoolStructFn(BoolStruct{false}); ret != false { | 324 | + | } |
| 412 | - | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | 325 | + | } |
| 413 | - | } | 326 | + | { |
| 414 | - | } | 327 | + | type UnsignedChar4BytesStruct struct { |
| 415 | - | { | 328 | + | x struct { |
| 416 | - | type BoolFloat struct { | 329 | + | a byte |
| 417 | - | b bool | 330 | + | } |
| 418 | - | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | 331 | + | y struct { |
| 419 | - | f float32 | 332 | + | b byte |
| 420 | - | } | 333 | + | } |
| 421 | - | var BoolFloatFn func(BoolFloat) float32 | 334 | + | z struct { |
| 422 | - | purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat") | 335 | + | c byte |
| 423 | - | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | 336 | + | } |
| 424 | - | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | 337 | + | w struct { |
| 425 | - | } | 338 | + | d byte |
| 426 | - | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | 339 | + | } |
| 427 | - | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | 340 | + | } |
| 428 | - | } | 341 | + | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 |
| 429 | - | } | 342 | + | register(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct", func(b UnsignedChar4BytesStruct) uint32 { |
| 430 | - | { | 343 | + | return uint32(b.x.a)<<24 | uint32(b.y.b)<<16 | uint32(b.z.c)<<8 | uint32(b.w.d) |
| 431 | - | type point struct{ x, y float64 } | 344 | + | }) |
| 432 | - | type size struct{ width, height float64 } | 345 | + | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ |
| 433 | - | type Content struct { | 346 | + | x: struct{ a byte }{a: 0xDE}, |
| 434 | - | point point | 347 | + | y: struct{ b byte }{b: 0xAD}, |
| 435 | - | size size | 348 | + | z: struct{ c byte }{c: 0xBE}, |
| 436 | - | } | 349 | + | w: struct{ d byte }{d: 0xEF}, |
| 437 | - | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | 350 | + | }); ret != expectedUnsigned { |
| 438 | - | purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect") | 351 | + | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) |
| 439 | - | if ret := InitWithContentRect(new(int), | 352 | + | } |
| 440 | - | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | 353 | + | } |
| 441 | - | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | 354 | + | { |
| 442 | - | 15, 4, true); ret != expectedUnsigned { | 355 | + | type Short struct { |
| 443 | - | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | 356 | + | a, b, c, d uint16 |
| 444 | - | } | 357 | + | } |
| 445 | - | } | 358 | + | var ShortFn func(Short) uint64 |
| 446 | - | { | 359 | + | register(&ShortFn, lib, "Short", func(s Short) uint64 { |
| 447 | - | type GoInt4 struct { | 360 | + | return uint64(s.a)<<48 | uint64(s.b)<<32 | uint64(s.c)<<16 | uint64(s.d) |
| 448 | - | A, B, C, D int | 361 | + | }) |
| 449 | - | } | 362 | + | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { |
| 450 | - | var GoInt4Fn func(GoInt4) int | 363 | + | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) |
| 451 | - | purego.RegisterLibFunc(&GoInt4Fn, lib, "GoInt4") | 364 | + | } |
| 452 | - | const expected = math.MaxInt - 52 - 3 + 4 | 365 | + | } |
| 453 | - | if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected { | 366 | + | { |
| 454 | - | t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected) | 367 | + | type Int struct { |
| 455 | - | } | 368 | + | a, b uint32 |
| 456 | - | } | 369 | + | } |
| 457 | - | { | 370 | + | var IntFn func(Int) uint64 |
| 458 | - | type GoUint4 struct { | 371 | + | register(&IntFn, lib, "Int", func(i Int) uint64 { |
| 459 | - | A, B, C, D uint | 372 | + | return uint64(i.a)<<32 | uint64(i.b) |
| 460 | - | } | 373 | + | }) |
| 461 | - | var GoUint4Fn func(GoUint4) uint | 374 | + | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { |
| 462 | - | purego.RegisterLibFunc(&GoUint4Fn, lib, "GoUint4") | 375 | + | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) |
| 463 | - | const expected = 1_000_000 + 53 + 71 + 8 | 376 | + | } |
| 464 | - | if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected { | 377 | + | } |
| 465 | - | t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected) | 378 | + | { |
| 466 | - | } | 379 | + | type Long struct { |
| 467 | - | } | 380 | + | a uint64 |
| 468 | - | { | 381 | + | } |
| 469 | - | type OneLong struct{ a uintptr } | 382 | + | var LongFn func(Long) uint64 |
| 470 | - | var TakeGoUintAndReturn func(a OneLong) uint64 | 383 | + | register(&LongFn, lib, "Long", func(l Long) uint64 { |
| 471 | - | purego.RegisterLibFunc(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn") | 384 | + | return l.a |
| 472 | - | expected := uint64(7) | 385 | + | }) |
| 473 | - | if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected { | 386 | + | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { |
| 474 | - | t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected) | 387 | + | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) |
| 475 | - | } | 388 | + | } |
| 476 | - | } | 389 | + | } |
| 477 | - | { | 390 | + | { |
| 478 | - | type FloatAndBool struct { | 391 | + | type Char8Bytes struct { |
| 479 | - | x float32 | 392 | + | a, b, c, d, e, f, g, h int8 |
| 480 | - | y bool | 393 | + | } |
| 481 | - | } | 394 | + | var Char8BytesFn func(Char8Bytes) int32 |
| 482 | - | var FloatAndBoolFn func(FloatAndBool) int32 | 395 | + | register(&Char8BytesFn, lib, "Char8Bytes", func(b Char8Bytes) int32 { |
| 483 | - | purego.RegisterLibFunc(&FloatAndBoolFn, lib, "FloatAndBool") | 396 | + | return int32(b.a) + int32(b.b) + int32(b.c) + int32(b.d) + int32(b.e) + int32(b.f) + int32(b.g) + int32(b.h) |
| 484 | - | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 { | 397 | + | }) |
| 485 | - | t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret) | 398 | + | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { |
| 486 | - | } | 399 | + | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) |
| 487 | - | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 { | 400 | + | } |
| 488 | - | t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret) | 401 | + | } |
| 489 | - | } | 402 | + | { |
| 490 | - | } | 403 | + | type Odd struct { |
| 491 | - | { | 404 | + | a, b, c byte |
| 492 | - | type FourInt32s struct { | 405 | + | } |
| 493 | - | f0, f1, f2, f3 int32 | 406 | + | var OddFn func(Odd) int32 |
| 494 | - | } | 407 | + | register(&OddFn, lib, "Odd", func(o Odd) int32 { |
| 495 | - | var FourInt32sFn func(FourInt32s) int32 | 408 | + | return int32(o.a) + int32(o.b) + int32(o.c) |
| 496 | - | purego.RegisterLibFunc(&FourInt32sFn, lib, "FourInt32s") | 409 | + | }) |
| 497 | - | result := FourInt32sFn(FourInt32s{100, -127, 4, -100}) | 410 | + | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { |
| 498 | - | const want = 100 - 127 + 4 - 100 | 411 | + | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) |
| 499 | - | if result != want { | 412 | + | } |
| 500 | - | t.Fatalf("FourInt32s returned %d wanted %d", result, want) | 413 | + | } |
| 501 | - | } | 414 | + | { |
| 502 | - | } | 415 | + | type Char2Short1 struct { |
| 503 | - | { | 416 | + | a, b byte |
| 504 | - | type PointerWrapper struct { | 417 | + | c uint16 |
| 505 | - | ctx unsafe.Pointer | 418 | + | } |
| 506 | - | } | 419 | + | var Char2Short1s func(Char2Short1) int32 |
| 507 | - | var ExtractPointer func(wrapper PointerWrapper) uintptr | 420 | + | register(&Char2Short1s, lib, "Char2Short1s", func(s Char2Short1) int32 { |
| 508 | - | purego.RegisterLibFunc(&ExtractPointer, lib, "ExtractPointer") | 421 | + | return int32(s.a) + int32(s.b) + int32(s.c) |
| 422 | + | }) | |||
| 423 | + | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | |||
| 424 | + | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | |||
| 425 | + | } | |||
| 426 | + | } | |||
| 427 | + | { | |||
| 428 | + | type SignedChar2Short1 struct { | |||
| 429 | + | a, b int8 | |||
| 430 | + | c int16 | |||
| 431 | + | } | |||
| 432 | + | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | |||
| 433 | + | register(&SignedChar2Short1Fn, lib, "SignedChar2Short1", func(s SignedChar2Short1) int32 { | |||
| 434 | + | return int32(s.a) + int32(s.b) + int32(s.c) | |||
| 435 | + | }) | |||
| 436 | + | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | |||
| 437 | + | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | |||
| 438 | + | } | |||
| 439 | + | } | |||
| 440 | + | { | |||
| 441 | + | type Array4UnsignedChars struct { | |||
| 442 | + | a [4]uint8 | |||
| 443 | + | } | |||
| 444 | + | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | |||
| 445 | + | register(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars", func(a Array4UnsignedChars) uint32 { | |||
| 446 | + | return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | uint32(a.a[3]) | |||
| 447 | + | }) | |||
| 448 | + | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | |||
| 449 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 450 | + | } | |||
| 451 | + | } | |||
| 452 | + | { | |||
| 453 | + | type Array3UnsignedChar struct { | |||
| 454 | + | a [3]uint8 | |||
| 455 | + | } | |||
| 456 | + | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | |||
| 457 | + | register(&Array3UnsignedChars, lib, "Array3UnsignedChars", func(a Array3UnsignedChar) uint32 { | |||
| 458 | + | return uint32(a.a[0])<<24 | uint32(a.a[1])<<16 | uint32(a.a[2])<<8 | 0xef | |||
| 459 | + | }) | |||
| 460 | + | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | |||
| 461 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 462 | + | } | |||
| 463 | + | } | |||
| 464 | + | { | |||
| 465 | + | type Array2UnsignedShort struct { | |||
| 466 | + | a [2]uint16 | |||
| 467 | + | } | |||
| 468 | + | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | |||
| 469 | + | register(&Array2UnsignedShorts, lib, "Array2UnsignedShorts", func(a Array2UnsignedShort) uint32 { | |||
| 470 | + | return uint32(a.a[0])<<16 | uint32(a.a[1]) | |||
| 471 | + | }) | |||
| 472 | + | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | |||
| 473 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 474 | + | } | |||
| 475 | + | } | |||
| 476 | + | { | |||
| 477 | + | type Array4Chars struct { | |||
| 478 | + | a [4]int8 | |||
| 479 | + | } | |||
| 480 | + | var Array4CharsFn func(chars Array4Chars) int32 | |||
| 481 | + | register(&Array4CharsFn, lib, "Array4Chars", func(a Array4Chars) int32 { | |||
| 482 | + | return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2]) + int32(a.a[3]) | |||
| 483 | + | }) | |||
| 484 | + | const expectedSum = 10 + 20 + 30 + 63 | |||
| 485 | + | if ret := Array4CharsFn(Array4Chars{a: [...]int8{10, 20, 30, 63}}); ret != expectedSum { | |||
| 486 | + | t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum) | |||
| 487 | + | } | |||
| 488 | + | } | |||
| 489 | + | { | |||
| 490 | + | type Array2Short struct { | |||
| 491 | + | a [2]int16 | |||
| 492 | + | } | |||
| 493 | + | var Array2Shorts func(chars Array2Short) int32 | |||
| 494 | + | register(&Array2Shorts, lib, "Array2Shorts", func(a Array2Short) int32 { | |||
| 495 | + | return int32(a.a[0]) + int32(a.a[1]) | |||
| 496 | + | }) | |||
| 497 | + | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | |||
| 498 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |||
| 499 | + | } | |||
| 500 | + | } | |||
| 501 | + | { | |||
| 502 | + | type Array3Short struct { | |||
| 503 | + | a [3]int16 | |||
| 504 | + | } | |||
| 505 | + | var Array3Shorts func(chars Array3Short) int32 | |||
| 506 | + | register(&Array3Shorts, lib, "Array3Shorts", func(a Array3Short) int32 { | |||
| 507 | + | return int32(a.a[0]) + int32(a.a[1]) + int32(a.a[2]) | |||
| 508 | + | }) | |||
| 509 | + | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | |||
| 510 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |||
| 511 | + | } | |||
| 512 | + | } | |||
| 513 | + | { | |||
| 514 | + | type BoolStruct struct { | |||
| 515 | + | b bool | |||
| 516 | + | } | |||
| 517 | + | var BoolStructFn func(BoolStruct) bool | |||
| 518 | + | register(&BoolStructFn, lib, "BoolStruct", func(b BoolStruct) bool { | |||
| 519 | + | return b.b | |||
| 520 | + | }) | |||
| 521 | + | if ret := BoolStructFn(BoolStruct{true}); ret != true { | |||
| 522 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | |||
| 523 | + | } | |||
| 524 | + | if ret := BoolStructFn(BoolStruct{false}); ret != false { | |||
| 525 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | |||
| 526 | + | } | |||
| 527 | + | } | |||
| 528 | + | { | |||
| 529 | + | type BoolFloat struct { | |||
| 530 | + | b bool | |||
| 531 | + | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | |||
| 532 | + | f float32 | |||
| 533 | + | } | |||
| 534 | + | var BoolFloatFn func(BoolFloat) float32 | |||
| 535 | + | register(&BoolFloatFn, lib, "BoolFloat", func(s BoolFloat) float32 { | |||
| 536 | + | if s.b { | |||
| 537 | + | return s.f | |||
| 538 | + | } | |||
| 539 | + | return -s.f | |||
| 540 | + | }) | |||
| 541 | + | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | |||
| 542 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | |||
| 543 | + | } | |||
| 544 | + | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | |||
| 545 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | |||
| 546 | + | } | |||
| 547 | + | } | |||
| 548 | + | { | |||
| 549 | + | type point struct{ x, y float64 } | |||
| 550 | + | type size struct{ width, height float64 } | |||
| 551 | + | type Content struct { | |||
| 552 | + | point point | |||
| 553 | + | size size | |||
| 554 | + | } | |||
| 555 | + | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | |||
| 556 | + | register(&InitWithContentRect, lib, "InitWithContentRect", func(win *int, c Content, style, backing int32, flag bool) uint64 { | |||
| 557 | + | if win == nil { | |||
| 558 | + | return 0xBAD | |||
| 559 | + | } | |||
| 560 | + | if !flag { | |||
| 561 | + | return 0xF1A6 | |||
| 562 | + | } | |||
| 563 | + | return uint64(c.point.x+c.point.y+c.size.width+c.size.height) / uint64(style-backing) | |||
| 564 | + | }) | |||
| 565 | + | if ret := InitWithContentRect(new(int), | |||
| 566 | + | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | |||
| 567 | + | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | |||
| 568 | + | 15, 4, true); ret != expectedUnsigned { | |||
| 569 | + | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | |||
| 570 | + | } | |||
| 571 | + | } | |||
| 572 | + | { | |||
| 573 | + | type GoInt4 struct { | |||
| 574 | + | A, B, C, D int | |||
| 575 | + | } | |||
| 576 | + | var GoInt4Fn func(GoInt4) int | |||
| 577 | + | register(&GoInt4Fn, lib, "GoInt4", func(g GoInt4) int { | |||
| 578 | + | return g.A + g.B - g.C + g.D | |||
| 579 | + | }) | |||
| 580 | + | const expected = math.MaxInt - 52 - 3 + 4 | |||
| 581 | + | if ret := GoInt4Fn(GoInt4{math.MaxInt, -52, 3, 4}); ret != expected { | |||
| 582 | + | t.Fatalf("GoInt4Fn returned %d wanted %#x", ret, expected) | |||
| 583 | + | } | |||
| 584 | + | } | |||
| 585 | + | { | |||
| 586 | + | type GoUint4 struct { | |||
| 587 | + | A, B, C, D uint | |||
| 588 | + | } | |||
| 589 | + | var GoUint4Fn func(GoUint4) uint | |||
| 590 | + | register(&GoUint4Fn, lib, "GoUint4", func(g GoUint4) uint { | |||
| 591 | + | return g.A + g.B + g.C + g.D | |||
| 592 | + | }) | |||
| 593 | + | const expected = 1_000_000 + 53 + 71 + 8 | |||
| 594 | + | if ret := GoUint4Fn(GoUint4{1_000_000, 53, 71, 8}); ret != expected { | |||
| 595 | + | t.Fatalf("GoUint4Fn returned %d wanted %#x", ret, expected) | |||
| 596 | + | } | |||
| 597 | + | } | |||
| 598 | + | { | |||
| 599 | + | type OneLong struct{ a uintptr } | |||
| 600 | + | var TakeGoUintAndReturn func(a OneLong) uint64 | |||
| 601 | + | register(&TakeGoUintAndReturn, lib, "TakeGoUintAndReturn", func(a OneLong) uint64 { | |||
| 602 | + | return uint64(a.a) | |||
| 603 | + | }) | |||
| 604 | + | expected := uint64(7) | |||
| 605 | + | if ret := TakeGoUintAndReturn(OneLong{7}); ret != expected { | |||
| 606 | + | t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected) | |||
| 607 | + | } | |||
| 608 | + | } | |||
| 609 | + | { | |||
| 610 | + | type FloatAndBool struct { | |||
| 611 | + | x float32 | |||
| 612 | + | y bool | |||
| 613 | + | } | |||
| 614 | + | var FloatAndBoolFn func(FloatAndBool) int32 | |||
| 615 | + | register(&FloatAndBoolFn, lib, "FloatAndBool", func(f FloatAndBool) int32 { | |||
| 616 | + | if f.y { | |||
| 617 | + | return 1 | |||
| 618 | + | } | |||
| 619 | + | return 0 | |||
| 620 | + | }) | |||
| 621 | + | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 { | |||
| 622 | + | t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret) | |||
| 623 | + | } | |||
| 624 | + | if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 { | |||
| 625 | + | t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret) | |||
| 626 | + | } | |||
| 627 | + | } | |||
| 628 | + | { | |||
| 629 | + | type FourInt32s struct { | |||
| 630 | + | f0, f1, f2, f3 int32 | |||
| 631 | + | } | |||
| 632 | + | var FourInt32sFn func(FourInt32s) int32 | |||
| 633 | + | register(&FourInt32sFn, lib, "FourInt32s", func(s FourInt32s) int32 { | |||
| 634 | + | return s.f0 + s.f1 + s.f2 + s.f3 | |||
| 635 | + | }) | |||
| 636 | + | result := FourInt32sFn(FourInt32s{100, -127, 4, -100}) | |||
| 637 | + | const want = 100 - 127 + 4 - 100 | |||
| 638 | + | if result != want { | |||
| 639 | + | t.Fatalf("FourInt32s returned %d wanted %d", result, want) | |||
| 640 | + | } | |||
| 641 | + | } | |||
| 642 | + | { | |||
| 643 | + | type PointerWrapper struct { | |||
| 644 | + | ctx unsafe.Pointer | |||
| 645 | + | } | |||
| 646 | + | var ExtractPointer func(wrapper PointerWrapper) uintptr | |||
| 647 | + | register(&ExtractPointer, lib, "ExtractPointer", func(wrapper PointerWrapper) uintptr { | |||
| 648 | + | return uintptr(wrapper.ctx) | |||
| 649 | + | }) | |||
| 509 | 650 | ||||
| 510 | - | var v int | 651 | + | var v int |
| 511 | - | ptr := unsafe.Pointer(&v) | 652 | + | ptr := unsafe.Pointer(&v) |
| 512 | - | expected := uintptr(ptr) | 653 | + | expected := uintptr(ptr) |
| 513 | - | result := ExtractPointer(PointerWrapper{ctx: ptr}) | 654 | + | result := ExtractPointer(PointerWrapper{ctx: ptr}) |
| 514 | - | if result != expected { | 655 | + | if result != expected { |
| 515 | - | t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected) | 656 | + | t.Fatalf("ExtractPointer returned %#x wanted %#x", result, expected) |
| 516 | - | } | 657 | + | } |
| 517 | - | } | 658 | + | } |
| 518 | - | { | 659 | + | { |
| 519 | - | type TwoPointers struct { | 660 | + | type TwoPointers struct { |
| 520 | - | ptr1, ptr2 unsafe.Pointer | 661 | + | ptr1, ptr2 unsafe.Pointer |
| 521 | - | } | 662 | + | } |
| 522 | - | var AddPointers func(wrapper TwoPointers) uintptr | 663 | + | var AddPointers func(wrapper TwoPointers) uintptr |
| 523 | - | purego.RegisterLibFunc(&AddPointers, lib, "AddPointers") | 664 | + | register(&AddPointers, lib, "AddPointers", func(wrapper TwoPointers) uintptr { |
| 665 | + | return uintptr(wrapper.ptr1) + uintptr(wrapper.ptr2) | |||
| 666 | + | }) | |||
| 524 | 667 | ||||
| 525 | - | var v1, v2 int | 668 | + | var v1, v2 int |
| 526 | - | ptr1 := unsafe.Pointer(&v1) | 669 | + | ptr1 := unsafe.Pointer(&v1) |
| 527 | - | ptr2 := unsafe.Pointer(&v2) | 670 | + | ptr2 := unsafe.Pointer(&v2) |
| 528 | - | expected := uintptr(ptr1) + uintptr(ptr2) | 671 | + | expected := uintptr(ptr1) + uintptr(ptr2) |
| 529 | - | result := AddPointers(TwoPointers{ptr1, ptr2}) | 672 | + | result := AddPointers(TwoPointers{ptr1, ptr2}) |
| 530 | - | if result != expected { | 673 | + | if result != expected { |
| 531 | - | t.Fatalf("AddPointers returned %#x wanted %#x", result, expected) | 674 | + | t.Fatalf("AddPointers returned %#x wanted %#x", result, expected) |
| 532 | - | } | 675 | + | } |
| 676 | + | } | |||
| 677 | + | ||||
| 678 | + | // Identity struct arg tests | |||
| 679 | + | { | |||
| 680 | + | // Single int64: one integer register. | |||
| 681 | + | type OneInt64 struct{ A int64 } | |||
| 682 | + | var fn func(OneInt64) OneInt64 | |||
| 683 | + | register(&fn, lib, "IdentityOneInt64", func(s OneInt64) OneInt64 { | |||
| 684 | + | return s | |||
| 685 | + | }) | |||
| 686 | + | expected := OneInt64{0xDEADBEEF} | |||
| 687 | + | if ret := fn(expected); ret != expected { | |||
| 688 | + | t.Fatalf("IdentityOneInt64 returned %+v wanted %+v", ret, expected) | |||
| 689 | + | } | |||
| 690 | + | } | |||
| 691 | + | { | |||
| 692 | + | // Two int64 fields: two integer registers. | |||
| 693 | + | type IntLessThan16Bytes struct{ X, Y int64 } | |||
| 694 | + | var fn func(IntLessThan16Bytes) IntLessThan16Bytes | |||
| 695 | + | register(&fn, lib, "IdentityIntLessThan16Bytes", func(s IntLessThan16Bytes) IntLessThan16Bytes { | |||
| 696 | + | return s | |||
| 697 | + | }) | |||
| 698 | + | expected := IntLessThan16Bytes{0xDEADBEEF, -42} | |||
| 699 | + | if ret := fn(expected); ret != expected { | |||
| 700 | + | t.Fatalf("IdentityIntLessThan16Bytes returned %+v wanted %+v", ret, expected) | |||
| 701 | + | } | |||
| 702 | + | } | |||
| 703 | + | { | |||
| 704 | + | // Two float64 fields: SSE on amd64, HFA on arm64. | |||
| 705 | + | type TwoDoubleStruct struct{ X, Y float64 } | |||
| 706 | + | var fn func(TwoDoubleStruct) TwoDoubleStruct | |||
| 707 | + | register(&fn, lib, "IdentityTwoDoubleStruct", func(s TwoDoubleStruct) TwoDoubleStruct { | |||
| 708 | + | return s | |||
| 709 | + | }) | |||
| 710 | + | expected := TwoDoubleStruct{3.14, -2.71} | |||
| 711 | + | if ret := fn(expected); ret != expected { | |||
| 712 | + | t.Fatalf("IdentityTwoDoubleStruct returned %+v wanted %+v", ret, expected) | |||
| 713 | + | } | |||
| 714 | + | } | |||
| 715 | + | { | |||
| 716 | + | // Four float32 fields: HFA on arm64, SSE on amd64. | |||
| 717 | + | type FourFloat32 struct{ A, B, C, D float32 } | |||
| 718 | + | var fn func(FourFloat32) FourFloat32 | |||
| 719 | + | register(&fn, lib, "IdentityFourFloat32", func(s FourFloat32) FourFloat32 { | |||
| 720 | + | return s | |||
| 721 | + | }) | |||
| 722 | + | expected := FourFloat32{1.0, 2.0, 3.0, 4.0} | |||
| 723 | + | if ret := fn(expected); ret != expected { | |||
| 724 | + | t.Fatalf("IdentityFourFloat32 returned %+v wanted %+v", ret, expected) | |||
| 725 | + | } | |||
| 726 | + | } | |||
| 727 | + | { | |||
| 728 | + | // Mixed float32 + int32: INTEGER class eightbyte on amd64, non-HFA int regs on arm64. | |||
| 729 | + | type FloatAndInt struct { | |||
| 730 | + | X float32 | |||
| 731 | + | Y int32 | |||
| 732 | + | } | |||
| 733 | + | var fn func(FloatAndInt) FloatAndInt | |||
| 734 | + | register(&fn, lib, "IdentityFloatAndInt", func(s FloatAndInt) FloatAndInt { | |||
| 735 | + | return s | |||
| 736 | + | }) | |||
| 737 | + | expected := FloatAndInt{7.5, 42} | |||
| 738 | + | if ret := fn(expected); ret != expected { | |||
| 739 | + | t.Fatalf("IdentityFloatAndInt returned %+v wanted %+v", ret, expected) | |||
| 740 | + | } | |||
| 741 | + | } | |||
| 742 | + | { | |||
| 743 | + | // Struct > 16 bytes: hidden pointer on amd64, pointer in int register on arm64. | |||
| 744 | + | type ThreeInt64 struct{ A, B, C int64 } | |||
| 745 | + | var fn func(ThreeInt64) ThreeInt64 | |||
| 746 | + | register(&fn, lib, "IdentityThreeInt64", func(s ThreeInt64) ThreeInt64 { | |||
| 747 | + | return s | |||
| 748 | + | }) | |||
| 749 | + | expected := ThreeInt64{1, 2, 3} | |||
| 750 | + | if ret := fn(expected); ret != expected { | |||
| 751 | + | t.Fatalf("IdentityThreeInt64 returned %+v wanted %+v", ret, expected) | |||
| 752 | + | } | |||
| 753 | + | } | |||
| 754 | + | { | |||
| 755 | + | // Struct > 16 bytes with pointer fields. | |||
| 756 | + | type PtrInt64Ptr struct { | |||
| 757 | + | A *int64 | |||
| 758 | + | B int64 | |||
| 759 | + | C *int64 | |||
| 760 | + | } | |||
| 761 | + | a, c := new(int64), new(int64) | |||
| 762 | + | *a, *c = 100, 300 | |||
| 763 | + | var fn func(PtrInt64Ptr) PtrInt64Ptr | |||
| 764 | + | register(&fn, lib, "IdentityPtrInt64Ptr", func(s PtrInt64Ptr) PtrInt64Ptr { | |||
| 765 | + | return s | |||
| 766 | + | }) | |||
| 767 | + | expected := PtrInt64Ptr{a, 200, c} | |||
| 768 | + | ret := fn(expected) | |||
| 769 | + | if *ret.A != *expected.A || ret.B != expected.B || *ret.C != *expected.C { | |||
| 770 | + | t.Fatalf("IdentityPtrInt64Ptr returned %+v wanted %+v", ret, expected) | |||
| 771 | + | } | |||
| 772 | + | runtime.KeepAlive(a) | |||
| 773 | + | runtime.KeepAlive(c) | |||
| 774 | + | } | |||
| 775 | + | { | |||
| 776 | + | // Struct arg after some primitive args: register offset correctness. | |||
| 777 | + | type IntLessThan16Bytes struct{ X, Y int64 } | |||
| 778 | + | var fn func(int64, float64, IntLessThan16Bytes) IntLessThan16Bytes | |||
| 779 | + | register(&fn, lib, "IdentityTwoInt64AfterPrims", func(x int64, y float64, s IntLessThan16Bytes) IntLessThan16Bytes { | |||
| 780 | + | return s | |||
| 781 | + | }) | |||
| 782 | + | expected := IntLessThan16Bytes{100, 200} | |||
| 783 | + | if ret := fn(1, 2.0, expected); ret != expected { | |||
| 784 | + | t.Fatalf("IdentityTwoInt64AfterPrims returned %+v wanted %+v", ret, expected) | |||
| 785 | + | } | |||
| 786 | + | } | |||
| 787 | + | { | |||
| 788 | + | // Float struct arg after float primitive args: float register offset. | |||
| 789 | + | type FloatLessThan16Bytes struct{ X, Y float32 } | |||
| 790 | + | var fn func(float64, float64, FloatLessThan16Bytes) FloatLessThan16Bytes | |||
| 791 | + | register(&fn, lib, "IdentityTwoFloat32AfterFloats", func(x, y float64, s FloatLessThan16Bytes) FloatLessThan16Bytes { | |||
| 792 | + | return s | |||
| 793 | + | }) | |||
| 794 | + | expected := FloatLessThan16Bytes{5.0, 6.0} | |||
| 795 | + | if ret := fn(1.0, 2.0, expected); ret != expected { | |||
| 796 | + | t.Fatalf("IdentityTwoFloat32AfterFloats returned %+v wanted %+v", ret, expected) | |||
| 797 | + | } | |||
| 798 | + | } | |||
| 799 | + | { | |||
| 800 | + | // Mixed struct with pointer: pointer + int32 + float32 + int32. | |||
| 801 | + | type Mixed5Args struct { | |||
| 802 | + | A *int64 | |||
| 803 | + | B int32 | |||
| 804 | + | C float32 | |||
| 805 | + | D int32 | |||
| 806 | + | } | |||
| 807 | + | ptr := new(int64) | |||
| 808 | + | *ptr = 99 | |||
| 809 | + | var fn func(Mixed5Args) Mixed5Args | |||
| 810 | + | register(&fn, lib, "IdentityMixed5Args", func(s Mixed5Args) Mixed5Args { | |||
| 811 | + | return s | |||
| 812 | + | }) | |||
| 813 | + | expected := Mixed5Args{ptr, 1, 7.2, 9} | |||
| 814 | + | ret := fn(expected) | |||
| 815 | + | if ret.A != expected.A || ret.B != expected.B || ret.C != expected.C || ret.D != expected.D { | |||
| 816 | + | t.Fatalf("IdentityMixed5Args returned %+v wanted %+v", ret, expected) | |||
| 817 | + | } | |||
| 818 | + | runtime.KeepAlive(ptr) | |||
| 819 | + | } | |||
| 820 | + | }) | |||
| 533 | } | 821 | } | ||
| 534 | } | 822 | } | ||
| 535 | 823 | ||||
| @@ -889,7 +1177,9 @@ func TestRegisterFunc_structReturns(t *testing.T) { | |||||
| 889 | D int32 | 1177 | D int32 | ||
| 890 | } | 1178 | } | ||
| 891 | var IdentityMixed5 func(m Mixed5) Mixed5 | 1179 | var IdentityMixed5 func(m Mixed5) Mixed5 | ||
| 892 | - | // TODO: when struct args are supported in NewCallback use register() instead | 1180 | + | // The GoCallbackFunc register helper decomposes args into struct fields, |
| 1181 | + | // which doesn't work for identity functions with struct args. | |||
| 1182 | + | // Struct callback args are tested in TestRegisterFunc_structArgs. | |||
| 893 | purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5") | 1183 | purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5") | ||
| 894 | ptr := new(int64) | 1184 | ptr := new(int64) | ||
| 895 | expected := Mixed5{ptr, 1, 7.2, 9} | 1185 | expected := Mixed5{ptr, 1, 7.2, 9} | ||
M syscall_unix.go +12 -3
| @@ -77,7 +77,8 @@ func compileCallback(fn any) uintptr { | |||
| 77 | 77 | continue | |
| 78 | 78 | } | |
| 79 | 79 | ensureStructSupported() | |
| 80 | - | fallthrough | |
| 80 | + | checkStructFieldsSupported(in) | |
| 81 | + | continue | |
| 81 | 82 | case reflect.Interface, reflect.Func, reflect.Slice, | |
| 82 | 83 | reflect.Chan, reflect.Complex64, reflect.Complex128, | |
| 83 | 84 | reflect.String, reflect.Map, reflect.Invalid: | |
| @@ -196,8 +197,16 @@ func callbackWrap(a *callbackArgs) { | |||
| 196 | 197 | } | |
| 197 | 198 | floatsN += slots | |
| 198 | 199 | case reflect.Struct: | |
| 199 | - | // This is the CDecl field | |
| 200 | - | args[i] = reflect.Zero(inType) | |
| 200 | + | if i == 0 && inType.AssignableTo(reflect.TypeOf(CDecl{})) { | |
| 201 | + | args[i] = reflect.Zero(inType) | |
| 202 | + | continue | |
| 203 | + | } | |
| 204 | + | if inType.Size() == 0 { | |
| 205 | + | args[i] = reflect.New(inType).Elem() | |
| 206 | + | continue | |
| 207 | + | } | |
| 208 | + | args[i] = getCallbackStruct(inType, a.args, &floatsN, &intsN, &stackSlot, &stackByteOffset) | |
| 209 | + | continue | |
| 201 | 210 | default: | |
| 202 | 211 | slots = int((inType.Size() + ptrSize - 1) / ptrSize) | |
| 203 | 212 | if intsN+slots > numOfIntegerRegisters() { | |
| @@ -77,7 +77,8 @@ func compileCallback(fn any) uintptr { | |||||
| 77 | continue | 77 | continue | ||
| 78 | } | 78 | } | ||
| 79 | ensureStructSupported() | 79 | ensureStructSupported() | ||
| 80 | - | fallthrough | 80 | + | checkStructFieldsSupported(in) |
| 81 | + | continue | |||
| 81 | case reflect.Interface, reflect.Func, reflect.Slice, | 82 | case reflect.Interface, reflect.Func, reflect.Slice, | ||
| 82 | reflect.Chan, reflect.Complex64, reflect.Complex128, | 83 | reflect.Chan, reflect.Complex64, reflect.Complex128, | ||
| 83 | reflect.String, reflect.Map, reflect.Invalid: | 84 | reflect.String, reflect.Map, reflect.Invalid: | ||
| @@ -196,8 +197,16 @@ func callbackWrap(a *callbackArgs) { | |||||
| 196 | } | 197 | } | ||
| 197 | floatsN += slots | 198 | floatsN += slots | ||
| 198 | case reflect.Struct: | 199 | case reflect.Struct: | ||
| 199 | - | // This is the CDecl field | 200 | + | if i == 0 && inType.AssignableTo(reflect.TypeOf(CDecl{})) { |
| 200 | - | args[i] = reflect.Zero(inType) | 201 | + | args[i] = reflect.Zero(inType) |
| 202 | + | continue | |||
| 203 | + | } | |||
| 204 | + | if inType.Size() == 0 { | |||
| 205 | + | args[i] = reflect.New(inType).Elem() | |||
| 206 | + | continue | |||
| 207 | + | } | |||
| 208 | + | args[i] = getCallbackStruct(inType, a.args, &floatsN, &intsN, &stackSlot, &stackByteOffset) | |||
| 209 | + | continue | |||
| 201 | default: | 210 | default: | ||
| 202 | slots = int((inType.Size() + ptrSize - 1) / ptrSize) | 211 | slots = int((inType.Size() + ptrSize - 1) / ptrSize) | ||
| 203 | if intsN+slots > numOfIntegerRegisters() { | 212 | if intsN+slots > numOfIntegerRegisters() { | ||
M syscall_windows.go +8 -0
| @@ -6,6 +6,7 @@ package purego | |||
| 6 | 6 | import ( | |
| 7 | 7 | "reflect" | |
| 8 | 8 | "syscall" | |
| 9 | + | "unsafe" | |
| 9 | 10 | ) | |
| 10 | 11 | ||
| 11 | 12 | var syscall15XABI0 uintptr | |
| @@ -44,3 +45,10 @@ func NewCallback(fn any) uintptr { | |||
| 44 | 45 | func loadSymbol(handle uintptr, name string) (uintptr, error) { | |
| 45 | 46 | return syscall.GetProcAddress(syscall.Handle(handle), name) | |
| 46 | 47 | } | |
| 48 | + | ||
| 49 | + | // callbackMaxFrame is a stub definition. | |
| 50 | + | const callbackMaxFrame = 0 | |
| 51 | + | ||
| 52 | + | func callbackArgFromStack(argsBase unsafe.Pointer, stackSlot int, stackByteOffset *uintptr, inType reflect.Type) reflect.Value { | |
| 53 | + | panic("purego: callbackArgFromStack should not be called on windows") | |
| 54 | + | } | |
| @@ -6,6 +6,7 @@ package purego | |||||
| 6 | import ( | 6 | import ( | ||
| 7 | "reflect" | 7 | "reflect" | ||
| 8 | "syscall" | 8 | "syscall" | ||
| 9 | + | "unsafe" | |||
| 9 | ) | 10 | ) | ||
| 10 | 11 | ||||
| 11 | var syscall15XABI0 uintptr | 12 | var syscall15XABI0 uintptr | ||
| @@ -44,3 +45,10 @@ func NewCallback(fn any) uintptr { | |||||
| 44 | func loadSymbol(handle uintptr, name string) (uintptr, error) { | 45 | func loadSymbol(handle uintptr, name string) (uintptr, error) { | ||
| 45 | return syscall.GetProcAddress(syscall.Handle(handle), name) | 46 | return syscall.GetProcAddress(syscall.Handle(handle), name) | ||
| 46 | } | 47 | } | ||
| 48 | + | ||||
| 49 | + | // callbackMaxFrame is a stub definition. | |||
| 50 | + | const callbackMaxFrame = 0 | |||
| 51 | + | ||||
| 52 | + | func callbackArgFromStack(argsBase unsafe.Pointer, stackSlot int, stackByteOffset *uintptr, inType reflect.Type) reflect.Value { | |||
| 53 | + | panic("purego: callbackArgFromStack should not be called on windows") | |||
| 54 | + | } | |||
M testdata/structtest/struct_test.c +67 -0
| @@ -391,3 +391,70 @@ struct TwoPointers { | |||
| 391 | 391 | uintptr_t AddPointers(struct TwoPointers wrapper) { | |
| 392 | 392 | return (uintptr_t)wrapper.ptr1 + (uintptr_t)wrapper.ptr2; | |
| 393 | 393 | } | |
| 394 | + | ||
| 395 | + | // Identity functions for round-trip testing of struct arguments | |
| 396 | + | ||
| 397 | + | struct OneInt64 { | |
| 398 | + | long long a; | |
| 399 | + | }; | |
| 400 | + | ||
| 401 | + | struct OneInt64 IdentityOneInt64(struct OneInt64 s) { | |
| 402 | + | return s; | |
| 403 | + | } | |
| 404 | + | ||
| 405 | + | struct IntLessThan16Bytes IdentityIntLessThan16Bytes(struct IntLessThan16Bytes s) { | |
| 406 | + | return s; | |
| 407 | + | } | |
| 408 | + | ||
| 409 | + | struct TwoDoubleStruct IdentityTwoDoubleStruct(struct TwoDoubleStruct s) { | |
| 410 | + | return s; | |
| 411 | + | } | |
| 412 | + | ||
| 413 | + | struct FourFloat32 { | |
| 414 | + | float a, b, c, d; | |
| 415 | + | }; | |
| 416 | + | ||
| 417 | + | struct FourFloat32 IdentityFourFloat32(struct FourFloat32 s) { | |
| 418 | + | return s; | |
| 419 | + | } | |
| 420 | + | ||
| 421 | + | struct FloatAndInt IdentityFloatAndInt(struct FloatAndInt s) { | |
| 422 | + | return s; | |
| 423 | + | } | |
| 424 | + | ||
| 425 | + | struct ThreeInt64 { | |
| 426 | + | long long a, b, c; | |
| 427 | + | }; | |
| 428 | + | ||
| 429 | + | struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) { | |
| 430 | + | return s; | |
| 431 | + | } | |
| 432 | + | ||
| 433 | + | struct PtrInt64Ptr { | |
| 434 | + | long long *a; | |
| 435 | + | long long b; | |
| 436 | + | long long *c; | |
| 437 | + | }; | |
| 438 | + | ||
| 439 | + | struct PtrInt64Ptr IdentityPtrInt64Ptr(struct PtrInt64Ptr s) { | |
| 440 | + | return s; | |
| 441 | + | } | |
| 442 | + | ||
| 443 | + | struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(long long x, double y, struct IntLessThan16Bytes s) { | |
| 444 | + | return s; | |
| 445 | + | } | |
| 446 | + | ||
| 447 | + | struct FloatLessThan16Bytes IdentityTwoFloat32AfterFloats(double x, double y, struct FloatLessThan16Bytes s) { | |
| 448 | + | return s; | |
| 449 | + | } | |
| 450 | + | ||
| 451 | + | struct Mixed5Args { | |
| 452 | + | long long *a; | |
| 453 | + | int32_t b; | |
| 454 | + | float c; | |
| 455 | + | int32_t d; | |
| 456 | + | }; | |
| 457 | + | ||
| 458 | + | struct Mixed5Args IdentityMixed5Args(struct Mixed5Args s) { | |
| 459 | + | return s; | |
| 460 | + | } | |
| @@ -391,3 +391,70 @@ struct TwoPointers { | |||||
| 391 | uintptr_t AddPointers(struct TwoPointers wrapper) { | 391 | uintptr_t AddPointers(struct TwoPointers wrapper) { | ||
| 392 | return (uintptr_t)wrapper.ptr1 + (uintptr_t)wrapper.ptr2; | 392 | return (uintptr_t)wrapper.ptr1 + (uintptr_t)wrapper.ptr2; | ||
| 393 | } | 393 | } | ||
| 394 | + | ||||
| 395 | + | // Identity functions for round-trip testing of struct arguments | |||
| 396 | + | ||||
| 397 | + | struct OneInt64 { | |||
| 398 | + | long long a; | |||
| 399 | + | }; | |||
| 400 | + | ||||
| 401 | + | struct OneInt64 IdentityOneInt64(struct OneInt64 s) { | |||
| 402 | + | return s; | |||
| 403 | + | } | |||
| 404 | + | ||||
| 405 | + | struct IntLessThan16Bytes IdentityIntLessThan16Bytes(struct IntLessThan16Bytes s) { | |||
| 406 | + | return s; | |||
| 407 | + | } | |||
| 408 | + | ||||
| 409 | + | struct TwoDoubleStruct IdentityTwoDoubleStruct(struct TwoDoubleStruct s) { | |||
| 410 | + | return s; | |||
| 411 | + | } | |||
| 412 | + | ||||
| 413 | + | struct FourFloat32 { | |||
| 414 | + | float a, b, c, d; | |||
| 415 | + | }; | |||
| 416 | + | ||||
| 417 | + | struct FourFloat32 IdentityFourFloat32(struct FourFloat32 s) { | |||
| 418 | + | return s; | |||
| 419 | + | } | |||
| 420 | + | ||||
| 421 | + | struct FloatAndInt IdentityFloatAndInt(struct FloatAndInt s) { | |||
| 422 | + | return s; | |||
| 423 | + | } | |||
| 424 | + | ||||
| 425 | + | struct ThreeInt64 { | |||
| 426 | + | long long a, b, c; | |||
| 427 | + | }; | |||
| 428 | + | ||||
| 429 | + | struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) { | |||
| 430 | + | return s; | |||
| 431 | + | } | |||
| 432 | + | ||||
| 433 | + | struct PtrInt64Ptr { | |||
| 434 | + | long long *a; | |||
| 435 | + | long long b; | |||
| 436 | + | long long *c; | |||
| 437 | + | }; | |||
| 438 | + | ||||
| 439 | + | struct PtrInt64Ptr IdentityPtrInt64Ptr(struct PtrInt64Ptr s) { | |||
| 440 | + | return s; | |||
| 441 | + | } | |||
| 442 | + | ||||
| 443 | + | struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(long long x, double y, struct IntLessThan16Bytes s) { | |||
| 444 | + | return s; | |||
| 445 | + | } | |||
| 446 | + | ||||
| 447 | + | struct FloatLessThan16Bytes IdentityTwoFloat32AfterFloats(double x, double y, struct FloatLessThan16Bytes s) { | |||
| 448 | + | return s; | |||
| 449 | + | } | |||
| 450 | + | ||||
| 451 | + | struct Mixed5Args { | |||
| 452 | + | long long *a; | |||
| 453 | + | int32_t b; | |||
| 454 | + | float c; | |||
| 455 | + | int32_t d; | |||
| 456 | + | }; | |||
| 457 | + | ||||
| 458 | + | struct Mixed5Args IdentityMixed5Args(struct Mixed5Args s) { | |||
| 459 | + | return s; | |||
| 460 | + | } | |||