github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/internal/ffi/ffi.go (about) 1 package ffi 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "unsafe" 8 ) 9 10 //go:linkname funcPC runtime.funcPC 11 func funcPC(f interface{}) uintptr 12 13 //go:linkname cgocall runtime.cgocall 14 func cgocall(fn, arg unsafe.Pointer) int32 15 16 //go:linkname _Cgo_always_false runtime.cgoAlwaysFalse 17 var _Cgo_always_false bool 18 19 //go:linkname _Cgo_use runtime.cgoUse 20 func _Cgo_use(interface{}) 21 22 type class int 23 24 const ( 25 classVoid = iota 26 classInt 27 classUint 28 classFloat 29 ) 30 31 type value struct { 32 offset int 33 c class 34 size int 35 align int 36 } 37 38 // stackFields takes pointer to function variable and returns: pointer to set it, argument offsets and type, and return value and type 39 // Arguments in go are according to the following (from cmd/compile/internal/gc/align.go dowidth TFUNCARGS): 40 // 3 consecutive structures on the stack 41 // 1. struct: receiver argument(s) 42 // 2. struct (aligned to register width): parameters 43 // 3. struct (aligned to register width): return values 44 func stackFields(fun interface{}) (fptr unsafe.Pointer, arguments []value, ret value, err error) { 45 v := reflect.ValueOf(fun) 46 if v.Kind() != reflect.Ptr { 47 err = errors.New("provided argument must be pointer to function variable") 48 return 49 } 50 f := v.Elem().Type() 51 if f.Kind() != reflect.Func { 52 err = errors.New("provided argument must be pointer to function variable") 53 return 54 } 55 if f.NumOut() > 1 { 56 err = errors.New("only one or no return argument allowed") 57 return 58 } 59 60 ret.c = classVoid 61 62 offset := 0 63 64 for i := 0; i < f.NumIn(); i++ { 65 a := f.In(i) 66 k := a.Kind() 67 var v value 68 69 skip := 0 70 71 v.size = int(a.Size()) 72 v.align = a.Align() 73 74 switch k { 75 case reflect.Slice: 76 v.size = int(unsafe.Sizeof(uintptr(0))) 77 skip = int(unsafe.Sizeof(reflect.SliceHeader{})) - v.size 78 v.c = classUint 79 case reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Bool: 80 v.c = classUint 81 case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: 82 v.c = classInt 83 case reflect.Float32, reflect.Float64: 84 v.c = classFloat 85 default: 86 err = fmt.Errorf("type %s of argument number %d not supported", k, i) 87 return 88 } 89 90 offset = (offset + v.align - 1) &^ (v.align - 1) 91 v.offset = offset 92 93 arguments = append(arguments, v) 94 95 offset += skip + v.size 96 } 97 98 if f.NumOut() == 1 { 99 a := f.Out(0) 100 k := a.Kind() 101 102 ret.size = int(a.Size()) 103 ret.align = int(unsafe.Sizeof(uintptr(0))) // return values are aligned by register size - let's hope this is the same as the pointer size 104 105 switch k { 106 case reflect.Slice: 107 ret.size = int(unsafe.Sizeof(uintptr(0))) 108 ret.c = classUint 109 case reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Bool: 110 ret.c = classUint 111 case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: 112 ret.c = classInt 113 case reflect.Float32, reflect.Float64: 114 ret.c = classFloat 115 default: 116 err = fmt.Errorf("type %s of return value not supported", k) 117 return 118 } 119 120 offset = (offset + ret.align - 1) &^ (ret.align - 1) 121 ret.offset = offset 122 } 123 124 fptr = unsafe.Pointer(v.Pointer()) 125 126 return 127 }