Mercurial > pub > dyncall > bindings
annotate 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 |
rev | line source |
---|---|
0 | 1 /* |
2 | |
3 godc.go | |
4 Copyright (c) 2014 Tassilo Philipp <tphilipp@potion-studios.com> | |
5 | |
6 Permission to use, copy, modify, and distribute this software for any | |
7 purpose with or without fee is hereby granted, provided that the above | |
8 copyright notice and this permission notice appear in all copies. | |
9 | |
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | |
18 */ | |
19 | |
20 | |
21 // Go/dyncall extension implementation. | |
22 package godc | |
23 | |
9
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
24 // #cgo LDFLAGS: -ldyncall_s |
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
25 // #cgo LDFLAGS: -ldynload_s |
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
26 // #cgo LDFLAGS: -ldyncallback_s |
0 | 27 // #include <stdlib.h> |
9
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
28 // #include "dyncall.h" |
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
29 // #include "dynload.h" |
8a45a05ff64e
- go binding cleanup, not enforcing relative paths anymore, readme update
cslag
parents:
0
diff
changeset
|
30 // #include "dyncall_signature.h" |
0 | 31 import "C" |
32 import ( | |
33 "unsafe" | |
34 "fmt" | |
35 "reflect" | |
36 ) | |
37 | |
38 type ExtLib struct { | |
39 lib *C.DLLib | |
40 syms *C.DLSyms | |
41 } | |
42 | |
43 type CallVM struct { | |
44 cvm *C.DCCallVM | |
45 } | |
46 | |
47 | |
48 // dynload | |
49 func (p *ExtLib) Load(path string) error { | |
50 s := C.CString(path) | |
51 defer C.free(unsafe.Pointer(s)) | |
52 p.lib = C.dlLoadLibrary(s) | |
53 if p.lib != nil { return nil } | |
54 return fmt.Errorf("Can't load %s", path) | |
55 } | |
56 | |
57 func (p *ExtLib) Free() { | |
58 C.dlFreeLibrary(p.lib) | |
59 } | |
60 | |
61 func (p *ExtLib) FindSymbol(name string) unsafe.Pointer { | |
62 s := C.CString(name) | |
63 defer C.free(unsafe.Pointer(s)) | |
64 return unsafe.Pointer(C.dlFindSymbol(p.lib, s)) | |
65 } | |
66 | |
67 | |
68 // dynload Syms | |
69 func (p *ExtLib) SymsInit(path string) error { | |
70 s := C.CString(path) | |
71 defer C.free(unsafe.Pointer(s)) | |
72 p.syms = C.dlSymsInit(s) | |
73 if p.syms != nil { return nil } | |
74 return fmt.Errorf("Can't load %s", path) | |
75 } | |
76 | |
77 func (p *ExtLib) SymsCleanup() { | |
78 C.dlSymsCleanup(p.syms) | |
79 } | |
80 | |
81 func (p *ExtLib) SymsCount() int { | |
82 return int(C.dlSymsCount(p.syms)) | |
83 } | |
84 | |
85 func (p *ExtLib) SymsName(i int) string { | |
86 return C.GoString(C.dlSymsName(p.syms, C.int(i))) | |
87 } | |
88 | |
89 func (p *ExtLib) SymsNameFromValue(v unsafe.Pointer) string { | |
90 return C.GoString(C.dlSymsNameFromValue(p.syms, v)) | |
91 } | |
92 | |
93 | |
94 // dyncall | |
95 func (p *CallVM) InitCallVM() error { | |
96 return p.InitCallVMWithStackSize(4096) | |
97 } | |
98 | |
99 func (p *CallVM) InitCallVMWithStackSize(stackSize int) error { | |
100 p.cvm = C.dcNewCallVM(C.DCsize(stackSize)) | |
101 if p.cvm != nil { return nil } | |
102 return fmt.Errorf("Can't create CallVM") | |
103 } | |
104 | |
105 func (p *CallVM) Free() { | |
106 C.dcFree(p.cvm) | |
107 } | |
108 | |
109 func (p *CallVM) Reset() { | |
110 C.dcReset(p.cvm) | |
111 } | |
112 | |
113 | |
114 | |
115 // Modes | |
116 const ( | |
117 DC_CALL_C_DEFAULT = C.DC_CALL_C_DEFAULT | |
118 DC_CALL_C_ELLIPSIS = C.DC_CALL_C_ELLIPSIS | |
119 DC_CALL_C_ELLIPSIS_VARARGS = C.DC_CALL_C_ELLIPSIS_VARARGS | |
120 DC_CALL_C_X86_CDECL = C.DC_CALL_C_X86_CDECL | |
121 DC_CALL_C_X86_WIN32_STD = C.DC_CALL_C_X86_WIN32_STD | |
122 DC_CALL_C_X86_WIN32_FAST_MS = C.DC_CALL_C_X86_WIN32_FAST_MS | |
123 DC_CALL_C_X86_WIN32_FAST_GNU = C.DC_CALL_C_X86_WIN32_FAST_GNU | |
124 DC_CALL_C_X86_WIN32_THIS_MS = C.DC_CALL_C_X86_WIN32_THIS_MS | |
125 DC_CALL_C_X86_WIN32_THIS_GNU = C.DC_CALL_C_X86_WIN32_THIS_GNU | |
126 DC_CALL_C_X64_WIN64 = C.DC_CALL_C_X64_WIN64 | |
127 DC_CALL_C_X64_SYSV = C.DC_CALL_C_X64_SYSV | |
128 DC_CALL_C_PPC32_DARWIN = C.DC_CALL_C_PPC32_DARWIN | |
129 DC_CALL_C_PPC32_OSX = C.DC_CALL_C_PPC32_OSX | |
130 DC_CALL_C_ARM_ARM_EABI = C.DC_CALL_C_ARM_ARM_EABI | |
131 DC_CALL_C_ARM_THUMB_EABI = C.DC_CALL_C_ARM_THUMB_EABI | |
132 DC_CALL_C_ARM_ARMHF = C.DC_CALL_C_ARM_ARMHF | |
133 DC_CALL_C_MIPS32_EABI = C.DC_CALL_C_MIPS32_EABI | |
134 DC_CALL_C_MIPS32_PSPSDK = C.DC_CALL_C_MIPS32_PSPSDK | |
135 DC_CALL_C_PPC32_SYSV = C.DC_CALL_C_PPC32_SYSV | |
136 DC_CALL_C_PPC32_LINUX = C.DC_CALL_C_PPC32_LINUX | |
137 DC_CALL_C_ARM_ARM = C.DC_CALL_C_ARM_ARM | |
138 DC_CALL_C_ARM_THUMB = C.DC_CALL_C_ARM_THUMB | |
139 DC_CALL_C_MIPS32_O32 = C.DC_CALL_C_MIPS32_O32 | |
140 DC_CALL_C_MIPS64_N32 = C.DC_CALL_C_MIPS64_N32 | |
141 DC_CALL_C_MIPS64_N64 = C.DC_CALL_C_MIPS64_N64 | |
142 DC_CALL_C_X86_PLAN9 = C.DC_CALL_C_X86_PLAN9 | |
143 DC_CALL_C_SPARC32 = C.DC_CALL_C_SPARC32 | |
144 DC_CALL_C_SPARC64 = C.DC_CALL_C_SPARC64 | |
145 DC_CALL_SYS_DEFAULT = C.DC_CALL_SYS_DEFAULT | |
146 DC_CALL_SYS_X86_INT80H_LINUX = C.DC_CALL_SYS_X86_INT80H_LINUX | |
147 DC_CALL_SYS_X86_INT80H_BSD = C.DC_CALL_SYS_X86_INT80H_BSD | |
148 ) | |
149 | |
150 | |
151 func (p *CallVM) Mode(mode int) { | |
152 C.dcMode(p.cvm, C.DCint(mode)) | |
153 } | |
154 | |
155 // Error codes | |
156 const ( | |
157 DC_ERROR_NONE = C.DC_ERROR_NONE | |
158 DC_ERROR_UNSUPPORTED_MODE = C.DC_ERROR_UNSUPPORTED_MODE | |
159 ) | |
160 | |
161 | |
162 func (p *CallVM) GetError() int { | |
163 return int(C.dcGetError(p.cvm)) | |
164 } | |
165 | |
166 | |
167 // Helper for string/pointer conversion, needed as low-level string alloc needs to be freed in different scope | |
168 func (p *CallVM) AllocCString(value string) unsafe.Pointer { s := C.CString(value); return unsafe.Pointer(s) } | |
169 func (p *CallVM) FreeCString (value unsafe.Pointer) { C.free(value) } | |
170 | |
171 // Args | |
172 func (p *CallVM) ArgBool (value bool) { if value==true { C.dcArgBool(p.cvm, C.DC_TRUE) } else { C.dcArgBool(p.cvm, C.DC_FALSE) } } | |
173 func (p *CallVM) ArgChar (value int8) { C.dcArgChar (p.cvm, C.DCchar (value)) } | |
174 func (p *CallVM) ArgShort (value int16) { C.dcArgShort (p.cvm, C.DCshort (value)) } | |
175 func (p *CallVM) ArgInt (value int) { C.dcArgInt (p.cvm, C.DCint (value)) } | |
176 func (p *CallVM) ArgLong (value int32) { C.dcArgLong (p.cvm, C.DClong (value)) } | |
177 func (p *CallVM) ArgLongLong (value int64) { C.dcArgLongLong(p.cvm, C.DClonglong(value)) } | |
178 func (p *CallVM) ArgFloat (value float32) { C.dcArgFloat (p.cvm, C.DCfloat (value)) } | |
179 func (p *CallVM) ArgDouble (value float64) { C.dcArgDouble (p.cvm, C.DCdouble (value)) } | |
180 func (p *CallVM) ArgPointer (value unsafe.Pointer) { C.dcArgPointer (p.cvm, C.DCpointer (value)) } | |
181 //@@@func (p *CallVM) ArgStruct (s C.DCstruct*, value unsafe.Pointer) | |
182 | |
183 // "Formatted" args | |
184 // - first takes Go's types (as they cover all C types dyncall supports) and pushes values accordingly | |
185 // - second uses a dyncall signature for implicit type conversion/casting, however it uses reflect package and is slower | |
186 // Note that first version doesn't feature calling convention mode switching. | |
187 func (p *CallVM) ArgF_Go(args ...interface{}) error { | |
188 | |
189 for i, n := 0, len(args); i<n; i++ { | |
190 switch args[i].(type) { | |
191 case bool: p.ArgBool ( (args[i].(bool ))) | |
192 case int8: p.ArgChar ( (args[i].(int8 ))) | |
193 case uint8/*byte*/: p.ArgChar (int8 (args[i].(uint8 ))) | |
194 case int16: p.ArgShort ( (args[i].(int16 ))) | |
195 case uint16: p.ArgShort (int16 (args[i].(uint16 ))) | |
196 case int: p.ArgInt ( (args[i].(int ))) | |
197 case uint: p.ArgInt (int (args[i].(uint ))) | |
198 case int32/*rune*/: p.ArgLong ( (args[i].(int32 ))) | |
199 case uint32: p.ArgLong (int32 (args[i].(uint32 ))) | |
200 case int64: p.ArgLongLong( (args[i].(int64 ))) | |
201 case uint64: p.ArgLongLong(int64 (args[i].(uint64 ))) | |
202 case float32: p.ArgFloat ( (args[i].(float32 ))) | |
203 case float64: p.ArgDouble ( (args[i].(float64 ))) | |
204 case uintptr: p.ArgPointer (unsafe.Pointer(args[i].(unsafe.Pointer))) | |
205 case unsafe.Pointer: p.ArgPointer ( (args[i].(unsafe.Pointer))) | |
206 default: return fmt.Errorf("Unknown type passed to ArgF_Go") | |
207 } | |
208 } | |
209 return nil | |
210 } | |
211 | |
212 func (p *CallVM) ArgF(signature string, args ...interface{}) { | |
213 | |
214 tb := reflect.TypeOf((*bool )(nil)).Elem() | |
215 ti := reflect.TypeOf((*int64 )(nil)).Elem() | |
216 tf := reflect.TypeOf((*float64)(nil)).Elem() | |
217 tp := reflect.TypeOf((*uintptr)(nil)).Elem() | |
218 | |
219 for i, n := 0, len(signature); i<n; i++ { | |
220 //@@@ add support for calling convention mode(s) | |
221 switch s := signature[i]; s { | |
222 case C.DC_SIGCHAR_BOOL: p.ArgBool (bool (reflect.ValueOf(args[i]).Convert(tb).Bool ())) | |
223 case C.DC_SIGCHAR_CHAR, C.DC_SIGCHAR_UCHAR: p.ArgChar (int8 (reflect.ValueOf(args[i]).Convert(ti).Int ())) | |
224 case C.DC_SIGCHAR_INT, C.DC_SIGCHAR_UINT: p.ArgInt (int (reflect.ValueOf(args[i]).Convert(ti).Int ())) | |
225 case C.DC_SIGCHAR_LONG, C.DC_SIGCHAR_ULONG: p.ArgLong (int32 (reflect.ValueOf(args[i]).Convert(ti).Int ())) | |
226 case C.DC_SIGCHAR_LONGLONG, C.DC_SIGCHAR_ULONGLONG: p.ArgLongLong(int64 (reflect.ValueOf(args[i]).Convert(ti).Int ())) | |
227 case C.DC_SIGCHAR_FLOAT: p.ArgFloat (float32 (reflect.ValueOf(args[i]).Convert(tf).Float ())) | |
228 case C.DC_SIGCHAR_DOUBLE: p.ArgDouble (float64 (reflect.ValueOf(args[i]).Convert(tf).Float ())) | |
229 case C.DC_SIGCHAR_POINTER, C.DC_SIGCHAR_STRING: p.ArgPointer (unsafe.Pointer(reflect.ValueOf(args[i]).Convert(tp).Pointer())) | |
230 case C.DC_SIGCHAR_ENDARG: return | |
231 } | |
232 // Faster, but doesn't do cross-type conversions. | |
233 //switch s := signature[i]; s { | |
234 // case C.DC_SIGCHAR_BOOL: p.ArgBool (args[i].(bool )) | |
235 // case C.DC_SIGCHAR_CHAR, C.DC_SIGCHAR_UCHAR: p.ArgChar (args[i].(int8 )) | |
236 // case C.DC_SIGCHAR_SHORT, C.DC_SIGCHAR_USHORT: p.ArgShort (args[i].(int16 )) | |
237 // case C.DC_SIGCHAR_INT, C.DC_SIGCHAR_UINT: p.ArgInt (args[i].(int )) | |
238 // case C.DC_SIGCHAR_LONG, C.DC_SIGCHAR_ULONG: p.ArgLong (args[i].(int32 )) | |
239 // case C.DC_SIGCHAR_LONGLONG, C.DC_SIGCHAR_ULONGLONG: p.ArgLongLong(args[i].(int64 )) | |
240 // case C.DC_SIGCHAR_FLOAT: p.ArgFloat (args[i].(float32 )) | |
241 // case C.DC_SIGCHAR_DOUBLE: p.ArgDouble (args[i].(float64 )) | |
242 // case C.DC_SIGCHAR_POINTER, C.DC_SIGCHAR_STRING: p.ArgPointer (args[i].(unsafe.Pointer)) | |
243 // case C.DC_SIGCHAR_ENDARG: return | |
244 //} | |
245 } | |
246 } | |
247 | |
248 | |
249 // Calls | |
250 func (p *CallVM) CallVoid (funcptr unsafe.Pointer) { C.dcCallVoid (p.cvm, C.DCpointer(funcptr)) } | |
251 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 } } | |
252 func (p *CallVM) CallChar (funcptr unsafe.Pointer) int8 { return int8 (C.dcCallChar (p.cvm, C.DCpointer(funcptr))) } | |
253 func (p *CallVM) CallShort (funcptr unsafe.Pointer) int16 { return int16 (C.dcCallShort (p.cvm, C.DCpointer(funcptr))) } | |
254 func (p *CallVM) CallInt (funcptr unsafe.Pointer) int { return int (C.dcCallInt (p.cvm, C.DCpointer(funcptr))) } | |
255 func (p *CallVM) CallLong (funcptr unsafe.Pointer) int32 { return int32 (C.dcCallLong (p.cvm, C.DCpointer(funcptr))) } | |
256 func (p *CallVM) CallLongLong (funcptr unsafe.Pointer) int64 { return int64 (C.dcCallLongLong(p.cvm, C.DCpointer(funcptr))) } | |
257 func (p *CallVM) CallFloat (funcptr unsafe.Pointer) float32 { return float32 (C.dcCallFloat (p.cvm, C.DCpointer(funcptr))) } | |
258 func (p *CallVM) CallDouble (funcptr unsafe.Pointer) float64 { return float64 (C.dcCallDouble (p.cvm, C.DCpointer(funcptr))) } | |
259 func (p *CallVM) CallPointer (funcptr unsafe.Pointer) unsafe.Pointer { return unsafe.Pointer(C.dcCallPointer (p.cvm, C.DCpointer(funcptr))) } | |
260 func (p *CallVM) CallPointerToStr(funcptr unsafe.Pointer) string { return C.GoString((*C.char)(C.dcCallPointer (p.cvm, C.DCpointer(funcptr)))) } // For convenience | |
261 //@@@func (p *CallVM) CallStruct (funcptr unsafe.Pointer, s C.DCstruct* s, returnValue unsafe.Pointer) | |
262 | |
263 // "Formatted" calls | |
264 //@@@func (p *CallVM) Call(result, funcptr unsafe.Pointer, signature string, args ...interface{}) { | |
265 //@@@... | |
266 //@@@} | |
267 | |
268 | |
269 //void dcCallF (DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, ...); | |
270 | |
271 /* | |
272 DC_API DCstruct* dcNewStruct (DCsize fieldCount, DCint alignment); | |
273 DC_API void dcStructField (DCstruct* s, DCint type, DCint alignment, DCsize arrayLength); | |
274 DC_API void dcSubStruct (DCstruct* s, DCsize fieldCount, DCint alignment, DCsize arrayLength); | |
275 DC_API void dcCloseStruct (DCstruct* s); | |
276 DC_API DCsize dcStructSize (DCstruct* s); | |
277 DC_API DCsize dcStructAlignment(DCstruct* s); | |
278 DC_API void dcFreeStruct (DCstruct* s); | |
279 | |
280 DC_API DCstruct* dcDefineStruct (const char* signature); | |
281 */ | |
282 |