github.com/notti/nocgo@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  }