Mercurial > pub > dyncall > bindings
view go/godc/godc.go @ 35:75fe1dec0eb4
- added support for signature-based calling convention switch
author | Tassilo Philipp |
---|---|
date | Mon, 13 Apr 2020 16:07:56 +0200 |
parents | 8a45a05ff64e |
children | 1e3d929e43be |
line wrap: on
line source
/* godc.go Copyright (c) 2014 Tassilo Philipp <tphilipp@potion-studios.com> Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // Go/dyncall extension implementation. package godc // #cgo LDFLAGS: -ldyncall_s // #cgo LDFLAGS: -ldynload_s // #cgo LDFLAGS: -ldyncallback_s // #include <stdlib.h> // #include "dyncall.h" // #include "dynload.h" // #include "dyncall_signature.h" import "C" import ( "unsafe" "fmt" "reflect" ) type ExtLib struct { lib *C.DLLib syms *C.DLSyms } type CallVM struct { cvm *C.DCCallVM } // dynload func (p *ExtLib) Load(path string) error { s := C.CString(path) defer C.free(unsafe.Pointer(s)) p.lib = C.dlLoadLibrary(s) if p.lib != nil { return nil } return fmt.Errorf("Can't load %s", path) } func (p *ExtLib) Free() { C.dlFreeLibrary(p.lib) } func (p *ExtLib) FindSymbol(name string) unsafe.Pointer { s := C.CString(name) defer C.free(unsafe.Pointer(s)) return unsafe.Pointer(C.dlFindSymbol(p.lib, s)) } // dynload Syms func (p *ExtLib) SymsInit(path string) error { s := C.CString(path) defer C.free(unsafe.Pointer(s)) p.syms = C.dlSymsInit(s) if p.syms != nil { return nil } return fmt.Errorf("Can't load %s", path) } func (p *ExtLib) SymsCleanup() { C.dlSymsCleanup(p.syms) } func (p *ExtLib) SymsCount() int { return int(C.dlSymsCount(p.syms)) } func (p *ExtLib) SymsName(i int) string { return C.GoString(C.dlSymsName(p.syms, C.int(i))) } func (p *ExtLib) SymsNameFromValue(v unsafe.Pointer) string { return C.GoString(C.dlSymsNameFromValue(p.syms, v)) } // dyncall func (p *CallVM) InitCallVM() error { return p.InitCallVMWithStackSize(4096) } func (p *CallVM) InitCallVMWithStackSize(stackSize int) error { p.cvm = C.dcNewCallVM(C.DCsize(stackSize)) if p.cvm != nil { return nil } return fmt.Errorf("Can't create CallVM") } func (p *CallVM) Free() { C.dcFree(p.cvm) } func (p *CallVM) Reset() { C.dcReset(p.cvm) } // Modes const ( DC_CALL_C_DEFAULT = C.DC_CALL_C_DEFAULT DC_CALL_C_ELLIPSIS = C.DC_CALL_C_ELLIPSIS DC_CALL_C_ELLIPSIS_VARARGS = C.DC_CALL_C_ELLIPSIS_VARARGS DC_CALL_C_X86_CDECL = C.DC_CALL_C_X86_CDECL DC_CALL_C_X86_WIN32_STD = C.DC_CALL_C_X86_WIN32_STD DC_CALL_C_X86_WIN32_FAST_MS = C.DC_CALL_C_X86_WIN32_FAST_MS DC_CALL_C_X86_WIN32_FAST_GNU = C.DC_CALL_C_X86_WIN32_FAST_GNU DC_CALL_C_X86_WIN32_THIS_MS = C.DC_CALL_C_X86_WIN32_THIS_MS DC_CALL_C_X86_WIN32_THIS_GNU = C.DC_CALL_C_X86_WIN32_THIS_GNU DC_CALL_C_X64_WIN64 = C.DC_CALL_C_X64_WIN64 DC_CALL_C_X64_SYSV = C.DC_CALL_C_X64_SYSV DC_CALL_C_PPC32_DARWIN = C.DC_CALL_C_PPC32_DARWIN DC_CALL_C_PPC32_OSX = C.DC_CALL_C_PPC32_OSX DC_CALL_C_ARM_ARM_EABI = C.DC_CALL_C_ARM_ARM_EABI DC_CALL_C_ARM_THUMB_EABI = C.DC_CALL_C_ARM_THUMB_EABI DC_CALL_C_ARM_ARMHF = C.DC_CALL_C_ARM_ARMHF DC_CALL_C_MIPS32_EABI = C.DC_CALL_C_MIPS32_EABI DC_CALL_C_MIPS32_PSPSDK = C.DC_CALL_C_MIPS32_PSPSDK DC_CALL_C_PPC32_SYSV = C.DC_CALL_C_PPC32_SYSV DC_CALL_C_PPC32_LINUX = C.DC_CALL_C_PPC32_LINUX DC_CALL_C_ARM_ARM = C.DC_CALL_C_ARM_ARM DC_CALL_C_ARM_THUMB = C.DC_CALL_C_ARM_THUMB DC_CALL_C_MIPS32_O32 = C.DC_CALL_C_MIPS32_O32 DC_CALL_C_MIPS64_N32 = C.DC_CALL_C_MIPS64_N32 DC_CALL_C_MIPS64_N64 = C.DC_CALL_C_MIPS64_N64 DC_CALL_C_X86_PLAN9 = C.DC_CALL_C_X86_PLAN9 DC_CALL_C_SPARC32 = C.DC_CALL_C_SPARC32 DC_CALL_C_SPARC64 = C.DC_CALL_C_SPARC64 DC_CALL_SYS_DEFAULT = C.DC_CALL_SYS_DEFAULT DC_CALL_SYS_X86_INT80H_LINUX = C.DC_CALL_SYS_X86_INT80H_LINUX DC_CALL_SYS_X86_INT80H_BSD = C.DC_CALL_SYS_X86_INT80H_BSD ) func (p *CallVM) Mode(mode int) { C.dcMode(p.cvm, C.DCint(mode)) } // Error codes const ( DC_ERROR_NONE = C.DC_ERROR_NONE DC_ERROR_UNSUPPORTED_MODE = C.DC_ERROR_UNSUPPORTED_MODE ) func (p *CallVM) GetError() int { return int(C.dcGetError(p.cvm)) } // Helper for string/pointer conversion, needed as low-level string alloc needs to be freed in different scope func (p *CallVM) AllocCString(value string) unsafe.Pointer { s := C.CString(value); return unsafe.Pointer(s) } func (p *CallVM) FreeCString (value unsafe.Pointer) { C.free(value) } // Args func (p *CallVM) ArgBool (value bool) { if value==true { C.dcArgBool(p.cvm, C.DC_TRUE) } else { C.dcArgBool(p.cvm, C.DC_FALSE) } } func (p *CallVM) ArgChar (value int8) { C.dcArgChar (p.cvm, C.DCchar (value)) } func (p *CallVM) ArgShort (value int16) { C.dcArgShort (p.cvm, C.DCshort (value)) } func (p *CallVM) ArgInt (value int) { C.dcArgInt (p.cvm, C.DCint (value)) } func (p *CallVM) ArgLong (value int32) { C.dcArgLong (p.cvm, C.DClong (value)) } func (p *CallVM) ArgLongLong (value int64) { C.dcArgLongLong(p.cvm, C.DClonglong(value)) } func (p *CallVM) ArgFloat (value float32) { C.dcArgFloat (p.cvm, C.DCfloat (value)) } func (p *CallVM) ArgDouble (value float64) { C.dcArgDouble (p.cvm, C.DCdouble (value)) } func (p *CallVM) ArgPointer (value unsafe.Pointer) { C.dcArgPointer (p.cvm, C.DCpointer (value)) } //@@@func (p *CallVM) ArgStruct (s C.DCstruct*, value unsafe.Pointer) // "Formatted" args // - first takes Go's types (as they cover all C types dyncall supports) and pushes values accordingly // - second uses a dyncall signature for implicit type conversion/casting, however it uses reflect package and is slower // Note that first version doesn't feature calling convention mode switching. func (p *CallVM) ArgF_Go(args ...interface{}) error { for i, n := 0, len(args); i<n; i++ { switch args[i].(type) { case bool: p.ArgBool ( (args[i].(bool ))) case int8: p.ArgChar ( (args[i].(int8 ))) case uint8/*byte*/: p.ArgChar (int8 (args[i].(uint8 ))) case int16: p.ArgShort ( (args[i].(int16 ))) case uint16: p.ArgShort (int16 (args[i].(uint16 ))) case int: p.ArgInt ( (args[i].(int ))) case uint: p.ArgInt (int (args[i].(uint ))) case int32/*rune*/: p.ArgLong ( (args[i].(int32 ))) case uint32: p.ArgLong (int32 (args[i].(uint32 ))) case int64: p.ArgLongLong( (args[i].(int64 ))) case uint64: p.ArgLongLong(int64 (args[i].(uint64 ))) case float32: p.ArgFloat ( (args[i].(float32 ))) case float64: p.ArgDouble ( (args[i].(float64 ))) case uintptr: p.ArgPointer (unsafe.Pointer(args[i].(unsafe.Pointer))) case unsafe.Pointer: p.ArgPointer ( (args[i].(unsafe.Pointer))) default: return fmt.Errorf("Unknown type passed to ArgF_Go") } } return nil } func (p *CallVM) ArgF(signature string, args ...interface{}) { tb := reflect.TypeOf((*bool )(nil)).Elem() ti := reflect.TypeOf((*int64 )(nil)).Elem() tf := reflect.TypeOf((*float64)(nil)).Elem() tp := reflect.TypeOf((*uintptr)(nil)).Elem() for i, n := 0, len(signature); i<n; i++ { //@@@ add support for calling convention mode(s) switch s := signature[i]; s { case C.DC_SIGCHAR_BOOL: p.ArgBool (bool (reflect.ValueOf(args[i]).Convert(tb).Bool ())) case C.DC_SIGCHAR_CHAR, C.DC_SIGCHAR_UCHAR: p.ArgChar (int8 (reflect.ValueOf(args[i]).Convert(ti).Int ())) case C.DC_SIGCHAR_INT, C.DC_SIGCHAR_UINT: p.ArgInt (int (reflect.ValueOf(args[i]).Convert(ti).Int ())) case C.DC_SIGCHAR_LONG, C.DC_SIGCHAR_ULONG: p.ArgLong (int32 (reflect.ValueOf(args[i]).Convert(ti).Int ())) case C.DC_SIGCHAR_LONGLONG, C.DC_SIGCHAR_ULONGLONG: p.ArgLongLong(int64 (reflect.ValueOf(args[i]).Convert(ti).Int ())) case C.DC_SIGCHAR_FLOAT: p.ArgFloat (float32 (reflect.ValueOf(args[i]).Convert(tf).Float ())) case C.DC_SIGCHAR_DOUBLE: p.ArgDouble (float64 (reflect.ValueOf(args[i]).Convert(tf).Float ())) case C.DC_SIGCHAR_POINTER, C.DC_SIGCHAR_STRING: p.ArgPointer (unsafe.Pointer(reflect.ValueOf(args[i]).Convert(tp).Pointer())) case C.DC_SIGCHAR_ENDARG: return } // Faster, but doesn't do cross-type conversions. //switch s := signature[i]; s { // case C.DC_SIGCHAR_BOOL: p.ArgBool (args[i].(bool )) // case C.DC_SIGCHAR_CHAR, C.DC_SIGCHAR_UCHAR: p.ArgChar (args[i].(int8 )) // case C.DC_SIGCHAR_SHORT, C.DC_SIGCHAR_USHORT: p.ArgShort (args[i].(int16 )) // case C.DC_SIGCHAR_INT, C.DC_SIGCHAR_UINT: p.ArgInt (args[i].(int )) // case C.DC_SIGCHAR_LONG, C.DC_SIGCHAR_ULONG: p.ArgLong (args[i].(int32 )) // case C.DC_SIGCHAR_LONGLONG, C.DC_SIGCHAR_ULONGLONG: p.ArgLongLong(args[i].(int64 )) // case C.DC_SIGCHAR_FLOAT: p.ArgFloat (args[i].(float32 )) // case C.DC_SIGCHAR_DOUBLE: p.ArgDouble (args[i].(float64 )) // case C.DC_SIGCHAR_POINTER, C.DC_SIGCHAR_STRING: p.ArgPointer (args[i].(unsafe.Pointer)) // case C.DC_SIGCHAR_ENDARG: return //} } } // Calls func (p *CallVM) CallVoid (funcptr unsafe.Pointer) { C.dcCallVoid (p.cvm, C.DCpointer(funcptr)) } func (p *CallVM) CallBool (funcptr unsafe.Pointer) bool { b := (C.dcCallBool(p.cvm, C.DCpointer(funcptr))); if b==C.DC_TRUE { return true } else { return false } } func (p *CallVM) CallChar (funcptr unsafe.Pointer) int8 { return int8 (C.dcCallChar (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallShort (funcptr unsafe.Pointer) int16 { return int16 (C.dcCallShort (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallInt (funcptr unsafe.Pointer) int { return int (C.dcCallInt (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallLong (funcptr unsafe.Pointer) int32 { return int32 (C.dcCallLong (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallLongLong (funcptr unsafe.Pointer) int64 { return int64 (C.dcCallLongLong(p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallFloat (funcptr unsafe.Pointer) float32 { return float32 (C.dcCallFloat (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallDouble (funcptr unsafe.Pointer) float64 { return float64 (C.dcCallDouble (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallPointer (funcptr unsafe.Pointer) unsafe.Pointer { return unsafe.Pointer(C.dcCallPointer (p.cvm, C.DCpointer(funcptr))) } func (p *CallVM) CallPointerToStr(funcptr unsafe.Pointer) string { return C.GoString((*C.char)(C.dcCallPointer (p.cvm, C.DCpointer(funcptr)))) } // For convenience //@@@func (p *CallVM) CallStruct (funcptr unsafe.Pointer, s C.DCstruct* s, returnValue unsafe.Pointer) // "Formatted" calls //@@@func (p *CallVM) Call(result, funcptr unsafe.Pointer, signature string, args ...interface{}) { //@@@... //@@@} //void dcCallF (DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, ...); /* DC_API DCstruct* dcNewStruct (DCsize fieldCount, DCint alignment); DC_API void dcStructField (DCstruct* s, DCint type, DCint alignment, DCsize arrayLength); DC_API void dcSubStruct (DCstruct* s, DCsize fieldCount, DCint alignment, DCsize arrayLength); DC_API void dcCloseStruct (DCstruct* s); DC_API DCsize dcStructSize (DCstruct* s); DC_API DCsize dcStructAlignment(DCstruct* s); DC_API void dcFreeStruct (DCstruct* s); DC_API DCstruct* dcDefineStruct (const char* signature); */