detached 1a798da

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 UnverifiedB5690EEEBB952194 The key is unknown
12 files · +1117 -486
M struct_386.go +8 -1
@@ -3,7 +3,10 @@
33
44 package purego
55
6-import "reflect"
6+import (
7+ "reflect"
8+ "unsafe"
9+)
710
811 func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
912 panic("purego: struct arguments are not supported")
@@ -40,6 +43,10 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
4043 panic("purego: bundleStackArgs should not be called on 386")
4144 }
4245
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+
4350 func setStruct(a *callbackArgs, ret reflect.Value) {
4451 panic("purego: struct returns are not supported on 386")
4552 }
@@ -3,7 +3,10 @@
3 3
4 package purego4 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
66 import (
77 "math"
88 "reflect"
9+ "runtime"
910 "unsafe"
1011 )
1112
@@ -285,6 +286,83 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
285286 panic("purego: bundleStackArgs should not be called on amd64")
286287 }
287288
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+
288366 func setStruct(a *callbackArgs, ret reflect.Value) {
289367 outSize := ret.Type().Size()
290368 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)) {
8484 panic("purego: bundleStackArgs should not be called on arm")
8585 }
8686
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+
8791 func setStruct(a *callbackArgs, ret reflect.Value) {
8892 panic("purego: struct returns are not supported on arm")
8993 }
@@ -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)) {
548548 copyStruct8ByteChunks(ptr, size, addStack)
549549 }
550550
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+
551703 func setStruct(a *callbackArgs, ret reflect.Value) {
552704 outSize := ret.Type().Size()
553705 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)) {
212212 panic("purego: bundleStackArgs should not be called on loong64")
213213 }
214214
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+
215219 func setStruct(a *callbackArgs, ret reflect.Value) {
216220 panic("purego: struct returns are not supported on loong64")
217221 }
@@ -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)) {
142142 panic("purego: bundleStackArgs should not be called on ppc64le")
143143 }
144144
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+
145149 func setStruct(a *callbackArgs, ret reflect.Value) {
146150 panic("purego: struct returns are not supported on ppc64le")
147151 }
@@ -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)) {
142142 panic("purego: bundleStackArgs should not be called on riscv64")
143143 }
144144
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+
145149 func setStruct(a *callbackArgs, ret reflect.Value) {
146150 panic("purego: struct returns are not supported on riscv64")
147151 }
@@ -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)) {
142142 panic("purego: bundleStackArgs should not be called on s390x")
143143 }
144144
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+
145149 func setStruct(a *callbackArgs, ret reflect.Value) {
146150 panic("purego: struct returns are not supported on s390x")
147151 }
@@ -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) {
4040 expectedDouble float64 = 10
4141 )
4242
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+ },
7469 }
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+ }
7983 }
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+ }
170100 }
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+ }
247113 }
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+ }
250128 }
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+ }
253157 }
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+ }
256169 }
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+ })
509650
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+ })
524667
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+ })
533821 }
534822 }
535823
@@ -889,7 +1177,9 @@ func TestRegisterFunc_structReturns(t *testing.T) {
8891177 D int32
8901178 }
8911179 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.
8931183 purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
8941184 ptr := new(int64)
8951185 expected := Mixed5{ptr, 1, 7.2, 9}
@@ -40,496 +40,784 @@ func TestRegisterFunc_structArgs(t *testing.T) {
40 expectedDouble float64 = 1040 expectedDouble float64 = 10
41 )41 )
42 42
43- {43+ implementations := []struct {
44- type Empty struct{}44+ name string
45- var NoStruct func(Empty) int6445+ 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) int6453+ {
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) int6458+ 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 *int6466+ purego.RegisterFunc(fptr, purego.NewCallback(goFn))
67- }67+ },
68- var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD000068+ },
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 *int6473+ 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, 0xDEAD000085+ type EmptyEmpty struct{}
82- var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int6486+ 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 *int6494+ 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, 0xDEAD000096+ })
93- var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int6497+ 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) float64102+ 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 float64110+ 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) float64112+ }
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 byte115+ 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 byte130+ 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 byte159+ 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) uint32171+ 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 uint16184+ x, y, z float32
272- }185+ }
273- var ShortFn func(Short) uint64186+ 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 uint32194+ {
282- }195+ type FloatAndInt struct {
283- var IntFn func(Int) uint64196+ 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 uint64204+ t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
292- }205+ }
293- var LongFn func(Long) uint64206+ }
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 int8214+ })
302- }215+ if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
303- var Char8BytesFn func(Char8Bytes) int32216+ 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 byte224+ register(&TwoDoubleStructFn, lib, "TwoDoubleStruct", func(d TwoDoubleStruct) float64 {
312- }225+ return d.x + d.y
313- var OddFn func(Odd) int32226+ })
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 byte234+ x, y float64
322- c uint16235+ }
323- }236+ }
324- var Char2Short1s func(Char2Short1) int32237+ 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 int8245+ {
333- c int16246+ type ThreeDoubleStruct struct {
334- }247+ x, y, z float64
335- var SignedChar2Short1Fn func(SignedChar2Short1) int32248+ }
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]uint8256+ }
344- }257+ {
345- var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32258+ 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]uint8266+ t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
354- }267+ }
355- var Array3UnsignedChars func(chars Array3UnsignedChar) uint32268+ 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]uint16276+ {
364- }277+ type Rect struct {
365- var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32278+ 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]int8286+ }
374- }287+ var RectangleSubtract func(rect Rect) float64
375- var Array4CharsFn func(chars Array4Chars) int32288+ 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 + 63290+ })
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]int16297+ })
385- }298+ if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
386- var Array2Shorts func(chars Array2Short) int32299+ 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]int16307+ register(&FloatArrayFn, lib, "FloatArray", func(f FloatArray) float64 {
395- }308+ return f.a[0] + f.a[1]
396- var Array3Shorts func(chars Array3Short) int32309+ })
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 bool317+ }
405- }318+ var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
406- var BoolStructFn func(BoolStruct) bool319+ 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 bool330+ }
418- _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct331+ y struct {
419- f float32332+ b byte
420- }333+ }
421- var BoolFloatFn func(BoolFloat) float32334+ 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 point347+ y: struct{ b byte }{b: 0xAD},
435- size size348+ z: struct{ c byte }{c: 0xBE},
436- }349+ w: struct{ d byte }{d: 0xEF},
437- var InitWithContentRect func(*int, Content, int32, int32, bool) uint64350+ }); 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 0xdeadbeef353+ }
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 int361+ })
449- }362+ if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
450- var GoInt4Fn func(GoInt4) int363+ t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
451- purego.RegisterLibFunc(&GoInt4Fn, lib, "GoInt4")364+ }
452- const expected = math.MaxInt - 52 - 3 + 4365+ }
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 uint372+ return uint64(i.a)<<32 | uint64(i.b)
460- }373+ })
461- var GoUint4Fn func(GoUint4) uint374+ 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 + 8376+ }
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) uint64383+ 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 float32392+ a, b, c, d, e, f, g, h int8
480- y bool393+ }
481- }394+ var Char8BytesFn func(Char8Bytes) int32
482- var FloatAndBoolFn func(FloatAndBool) int32395+ 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 int32406+ var OddFn func(Odd) int32
494- }407+ register(&OddFn, lib, "Odd", func(o Odd) int32 {
495- var FourInt32sFn func(FourInt32s) int32408+ 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 - 100411+ 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.Pointer418+ }
506- }419+ var Char2Short1s func(Char2Short1) int32
507- var ExtractPointer func(wrapper PointerWrapper) uintptr420+ 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 int651+ 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.Pointer661+ ptr1, ptr2 unsafe.Pointer
521- }662+ }
522- var AddPointers func(wrapper TwoPointers) uintptr663+ 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 int668+ 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 int321177 D int32
890 }1178 }
891 var IdentityMixed5 func(m Mixed5) Mixed51179 var IdentityMixed5 func(m Mixed5) Mixed5
892- // TODO: when struct args are supported in NewCallback use register() instead1180+ // 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 {
7777 continue
7878 }
7979 ensureStructSupported()
80- fallthrough
80+ checkStructFieldsSupported(in)
81+ continue
8182 case reflect.Interface, reflect.Func, reflect.Slice,
8283 reflect.Chan, reflect.Complex64, reflect.Complex128,
8384 reflect.String, reflect.Map, reflect.Invalid:
@@ -196,8 +197,16 @@ func callbackWrap(a *callbackArgs) {
196197 }
197198 floatsN += slots
198199 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
201210 default:
202211 slots = int((inType.Size() + ptrSize - 1) / ptrSize)
203212 if intsN+slots > numOfIntegerRegisters() {
@@ -77,7 +77,8 @@ func compileCallback(fn any) uintptr {
77 continue77 continue
78 }78 }
79 ensureStructSupported()79 ensureStructSupported()
80- fallthrough80+ 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 += slots198 floatsN += slots
198 case reflect.Struct:199 case reflect.Struct:
199- // This is the CDecl field200+ 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
66 import (
77 "reflect"
88 "syscall"
9+ "unsafe"
910 )
1011
1112 var syscall15XABI0 uintptr
@@ -44,3 +45,10 @@ func NewCallback(fn any) uintptr {
4445 func loadSymbol(handle uintptr, name string) (uintptr, error) {
4546 return syscall.GetProcAddress(syscall.Handle(handle), name)
4647 }
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 uintptr12 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 {
391391 uintptr_t AddPointers(struct TwoPointers wrapper) {
392392 return (uintptr_t)wrapper.ptr1 + (uintptr_t)wrapper.ptr2;
393393 }
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+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close