Mercurial > pub > dyncall > bindings
annotate ruby/rbdc/rbdc.c @ 35:75fe1dec0eb4
- added support for signature-based calling convention switch
author | Tassilo Philipp |
---|---|
date | Mon, 13 Apr 2020 16:07:56 +0200 |
parents | 02a455de2b40 |
children |
rev | line source |
---|---|
0 | 1 /* |
2 | |
3 rbdc.c | |
25 | 4 Copyright (c) 2007-2018 Daniel Adler <dadler@uni-goettingen.de>, |
0 | 5 Tassilo Philipp <tphilipp@potion-studios.com> |
6 | |
7 Permission to use, copy, modify, and distribute this software for any | |
8 purpose with or without fee is hereby granted, provided that the above | |
9 copyright notice and this permission notice appear in all copies. | |
10 | |
11 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | |
19 Ruby/dyncall extension implementation. | |
20 | |
21 */ | |
22 | |
23 | |
24 #include <ruby.h> | |
6
80273969f043
- ruby binding path cleanup, previous version required bindings and dyncall be checked out in same parent directory
cslag
parents:
1
diff
changeset
|
25 #include "dyncall/dyncall/dyncall.h" |
80273969f043
- ruby binding path cleanup, previous version required bindings and dyncall be checked out in same parent directory
cslag
parents:
1
diff
changeset
|
26 #include "dyncall/dyncallback/dyncall_callback.h" |
80273969f043
- ruby binding path cleanup, previous version required bindings and dyncall be checked out in same parent directory
cslag
parents:
1
diff
changeset
|
27 #include "dyncall/dynload/dynload.h" |
80273969f043
- ruby binding path cleanup, previous version required bindings and dyncall be checked out in same parent directory
cslag
parents:
1
diff
changeset
|
28 #include "dyncall/dyncall/dyncall_signature.h" |
0 | 29 |
30 /* Our ruby module and its classes. */ | |
31 static VALUE rb_dcModule; | |
32 static VALUE rb_dcExtLib; | |
33 | |
34 | |
35 typedef struct { | |
36 void* lib; | |
37 void* syms; | |
38 DCCallVM* cvm; | |
39 } rb_dcLibHandle; | |
40 | |
41 | |
42 /* Allocator for handle and mark-and-sweep GC handlers. */ | |
43 static void GCMark_ExtLib(rb_dcLibHandle* h) | |
44 { | |
45 } | |
46 | |
47 static void GCSweep_ExtLib(rb_dcLibHandle* h) | |
48 { | |
49 if(h->lib != NULL) dlFreeLibrary(h->lib); | |
50 if(h->syms != NULL) dlSymsCleanup(h->syms); | |
51 | |
52 dcFree(h->cvm); | |
53 free(h); | |
54 } | |
55 | |
56 static VALUE AllocExtLib(VALUE cl) | |
57 { | |
58 rb_dcLibHandle* h = malloc(sizeof(rb_dcLibHandle)); | |
59 h->lib = NULL; | |
60 h->syms = NULL; | |
61 h->cvm = dcNewCallVM(4096/*@@@*/); | |
62 return Data_Wrap_Struct(cl, GCMark_ExtLib, GCSweep_ExtLib, h); | |
63 } | |
64 | |
65 | |
66 /* Helpers */ | |
67 static void ExtLib_SecCheckLib(rb_dcLibHandle* h) | |
68 { | |
69 if(h->lib == NULL) | |
70 rb_raise(rb_eRuntimeError, "no library loaded - use ExtLib#load"); | |
71 } | |
72 | |
73 static void ExtLib_SecCheckSyms(rb_dcLibHandle* h) | |
74 { | |
75 if(h->syms == NULL) | |
76 rb_raise(rb_eRuntimeError, "no symbol table initialized - use ExtLib#symsInit"); | |
77 } | |
78 | |
79 | |
80 | |
81 /* Methods for lib access */ | |
82 static VALUE ExtLib_Load(VALUE self, VALUE path) | |
83 { | |
84 void* newLib; | |
85 rb_dcLibHandle* h; | |
86 | |
87 if(TYPE(path) != T_STRING) | |
88 rb_raise(rb_eRuntimeError, "argument must be of type 'String'");/*@@@ respond to to_s*/ | |
89 | |
90 Data_Get_Struct(self, rb_dcLibHandle, h); | |
91 newLib = dlLoadLibrary(RSTRING_PTR(path)); | |
92 if(newLib) { | |
93 dlFreeLibrary(h->lib); | |
94 h->lib = newLib; | |
95 | |
96 return self; | |
97 } | |
98 | |
99 return Qnil; | |
100 } | |
101 | |
102 static VALUE ExtLib_ExistsQ(VALUE self, VALUE sym) | |
103 { | |
104 rb_dcLibHandle* h; | |
105 | |
106 Data_Get_Struct(self, rb_dcLibHandle, h); | |
107 ExtLib_SecCheckLib(h); | |
108 | |
109 return dlFindSymbol(h->lib, rb_id2name(SYM2ID(sym))) ? Qtrue : Qfalse; | |
110 } | |
111 | |
112 | |
113 /* Methods for syms parsing */ | |
114 static VALUE ExtLib_SymsInit(VALUE self, VALUE path) | |
115 { | |
116 void* newSyms; | |
117 rb_dcLibHandle* h; | |
118 | |
119 if(TYPE(path) != T_STRING) | |
120 rb_raise(rb_eRuntimeError, "argument must be of type 'String'");/*@@@ respond to to_s*/ | |
121 | |
122 Data_Get_Struct(self, rb_dcLibHandle, h); | |
123 newSyms = dlSymsInit(RSTRING_PTR(path)); | |
124 | |
125 if(newSyms) { | |
126 dlSymsCleanup(h->syms); | |
127 h->syms = newSyms; | |
128 | |
129 return self; | |
130 } | |
131 | |
132 return Qnil; | |
133 } | |
134 | |
135 | |
136 static VALUE ExtLib_SymsCount(VALUE self) | |
137 { | |
138 rb_dcLibHandle* h; | |
139 | |
140 Data_Get_Struct(self, rb_dcLibHandle, h); | |
141 ExtLib_SecCheckSyms(h); | |
142 | |
143 return LONG2NUM(dlSymsCount(h->syms)); | |
144 } | |
145 | |
146 | |
147 static VALUE ExtLib_SymsEach(int argc, VALUE* argv, VALUE self) | |
148 { | |
149 rb_dcLibHandle* h; | |
150 size_t i, c; | |
151 | |
152 if(!rb_block_given_p()) | |
153 rb_raise(rb_eRuntimeError, "no block given"); | |
154 | |
155 Data_Get_Struct(self, rb_dcLibHandle, h); | |
156 ExtLib_SecCheckSyms(h); | |
157 | |
158 c = dlSymsCount(h->syms); | |
159 for(i=0; i<c; ++i) | |
160 rb_yield(ID2SYM(rb_intern(dlSymsName(h->syms, i)))); | |
161 | |
162 return self; | |
163 } | |
164 | |
165 /* expose dlSymsName @@@ */ | |
166 | |
167 | |
168 /* Methods interfacing with dyncall */ | |
169 static VALUE ExtLib_Call(int argc, VALUE* argv, VALUE self) | |
170 { | |
171 /* argv[0] - symbol to call * | |
172 * argv[1] - signature * | |
173 * argv[2] - first parameter * | |
174 * argv[x] - parameter x-2 */ | |
175 | |
176 rb_dcLibHandle* h; | |
177 DCpointer fptr; | |
178 int i, t, b; | |
179 VALUE r; | |
180 DCCallVM* cvm; | |
181 const char* sig; | |
182 | |
183 | |
184 /* Security checks. */ | |
185 if(argc < 2) | |
186 rb_raise(rb_eRuntimeError, "wrong number of arguments for function call"); | |
187 | |
188 if(TYPE(argv[0]) != T_SYMBOL) | |
189 rb_raise(rb_eRuntimeError, "syntax error - argument 0 must be of type 'Symbol'"); | |
190 | |
191 if(TYPE(argv[1]) != T_STRING) | |
192 rb_raise(rb_eRuntimeError, "syntax error - argument 1 must be of type 'String'"); | |
193 | |
194 Data_Get_Struct(self, rb_dcLibHandle, h); | |
195 cvm = h->cvm; | |
196 | |
197 if(argc != RSTRING_LEN(argv[1])) /* Don't count the return value in the signature @@@ write something more secure */ | |
198 rb_raise(rb_eRuntimeError, "number of provided arguments doesn't match signature"); | |
199 | |
200 ExtLib_SecCheckLib(h); | |
201 | |
202 | |
203 /* Flush old arguments. */ | |
204 dcReset(cvm); | |
205 | |
206 | |
207 /* Get a pointer to the function and start pushing. */ | |
208 fptr = (DCpointer)dlFindSymbol(h->lib, rb_id2name(SYM2ID(argv[0]))); | |
209 sig = RSTRING_PTR(argv[1]); | |
210 | |
211 for(i=2; i<argc; ++i) { | |
212 t = TYPE(argv[i]); | |
213 | |
214 //@@@ add support for calling convention mode(s) | |
215 | |
216 switch(sig[i-2]) { | |
217 case DC_SIGCHAR_BOOL: | |
218 b = 1; | |
219 switch(t) { | |
220 case T_TRUE: dcArgBool(cvm, DC_TRUE); break; /* TrueClass. */ | |
221 case T_FALSE: /* FalseClass. */ | |
222 case T_NIL: dcArgBool(cvm, DC_FALSE); break; /* NilClass. */ | |
223 case T_FIXNUM: dcArgBool(cvm, FIX2LONG(argv[i]) != 0); break; /* Fixnum. */ | |
224 default: b = 0; break; | |
225 } | |
226 break; | |
227 | |
228 case DC_SIGCHAR_CHAR: | |
229 case DC_SIGCHAR_UCHAR: if(b = (t == T_FIXNUM)) dcArgChar (cvm, (DCchar) FIX2LONG(argv[i])); break; | |
230 case DC_SIGCHAR_SHORT: | |
231 case DC_SIGCHAR_USHORT: if(b = (t == T_FIXNUM)) dcArgShort (cvm, (DCshort) FIX2LONG(argv[i])); break; | |
232 case DC_SIGCHAR_INT: | |
233 case DC_SIGCHAR_UINT: if(b = (t == T_FIXNUM)) dcArgInt (cvm, (DCint) FIX2LONG(argv[i])); break; | |
234 case DC_SIGCHAR_LONG: | |
235 case DC_SIGCHAR_ULONG: if(b = (t == T_FIXNUM)) dcArgLong (cvm, (DClong) FIX2LONG(argv[i])); break; | |
236 case DC_SIGCHAR_LONGLONG: | |
237 case DC_SIGCHAR_ULONGLONG: if(b = (t == T_FIXNUM)) dcArgLongLong(cvm, (DClonglong)FIX2LONG(argv[i])); break; | |
238 case DC_SIGCHAR_FLOAT: if(b = (t == T_FLOAT)) dcArgFloat (cvm, (DCfloat) RFLOAT_VALUE(argv[i])); break; | |
239 case DC_SIGCHAR_DOUBLE: if(b = (t == T_FLOAT)) dcArgDouble (cvm, (DCdouble) RFLOAT_VALUE(argv[i])); break; | |
240 | |
241 case DC_SIGCHAR_POINTER: | |
242 case DC_SIGCHAR_STRING: | |
243 b = 1; | |
244 switch(t) { | |
245 case T_STRING: dcArgPointer(cvm, RSTRING_PTR(argv[i])); break; /* String. */ | |
246 default: b = 0; break; | |
247 } | |
248 break; | |
249 | |
250 default: | |
251 b = 0; | |
252 break; | |
253 } | |
254 | |
255 | |
256 if(!b) | |
257 rb_raise(rb_eRuntimeError, "syntax error in signature or type mismatch at argument %d", i-2); | |
258 } | |
259 | |
260 | |
261 /* Get the return type and call the function. */ | |
262 switch(sig[i-1]) { | |
263 case DC_SIGCHAR_VOID: r = Qnil; dcCallVoid (cvm, fptr); break; | |
264 case DC_SIGCHAR_BOOL: r = dcCallBool (cvm, fptr) ? Qtrue : Qfalse; break; | |
265 case DC_SIGCHAR_CHAR: | |
266 case DC_SIGCHAR_UCHAR: r = CHR2FIX( dcCallChar (cvm, fptr)); break; | |
267 case DC_SIGCHAR_SHORT: | |
268 case DC_SIGCHAR_USHORT: r = INT2FIX( dcCallShort (cvm, fptr)); break; | |
269 case DC_SIGCHAR_INT: | |
270 case DC_SIGCHAR_UINT: r = INT2FIX( dcCallInt (cvm, fptr)); break; | |
271 case DC_SIGCHAR_LONG: | |
272 case DC_SIGCHAR_ULONG: r = INT2FIX( dcCallLong (cvm, fptr)); break; | |
273 case DC_SIGCHAR_LONGLONG: | |
274 case DC_SIGCHAR_ULONGLONG: r = INT2FIX( dcCallLongLong(cvm, fptr)); break; | |
275 case DC_SIGCHAR_FLOAT: r = rb_float_new(dcCallFloat (cvm, fptr)); break; | |
276 case DC_SIGCHAR_DOUBLE: r = rb_float_new(dcCallDouble (cvm, fptr)); break; | |
277 case DC_SIGCHAR_STRING: r = rb_str_new2( dcCallPointer (cvm, fptr)); break; | |
278 case DC_SIGCHAR_POINTER: | |
279 default: | |
280 rb_raise(rb_eRuntimeError, "unsupported return type or syntax error in signature"); | |
281 } | |
282 | |
283 return r; | |
284 } | |
285 | |
286 | |
287 /* Main initialization. */ | |
288 void Init_rbdc() | |
289 { | |
290 rb_dcModule = rb_define_module("Dyncall"); | |
291 | |
292 /* Handle to the external dynamic library. */ | |
293 rb_dcExtLib = rb_define_class_under(rb_dcModule, "ExtLib", rb_cObject); | |
294 | |
295 /* Class allocators. */ | |
296 rb_define_alloc_func(rb_dcExtLib, AllocExtLib); | |
297 | |
298 /* Methods. */ | |
299 rb_define_method(rb_dcExtLib, "load", &ExtLib_Load, 1); | |
300 rb_define_method(rb_dcExtLib, "exists?", &ExtLib_ExistsQ, 1); | |
301 rb_define_method(rb_dcExtLib, "syms_init", &ExtLib_SymsInit, 1); | |
302 rb_define_method(rb_dcExtLib, "syms_count", &ExtLib_SymsCount, 0); | |
303 rb_define_method(rb_dcExtLib, "syms_each", &ExtLib_SymsEach, -1); | |
304 rb_define_method(rb_dcExtLib, "call", &ExtLib_Call, -1); | |
305 } | |
306 |