1 module orelang.operator.DllOperator;
2 import orelang.operator.IOperator, orelang.Engine, orelang.Value;
3 import orelang.Util;
4 import core.sys.posix.dlfcn;
5 import std..string, std.stdio, std.format, std.conv;
6 
7 version (WithFFI) {
8   enum D_TYPE {
9     VOID,
10     UBYTE,
11     BYTE,
12     USHORT,
13     SHORT,
14     UINT,
15     INT,
16     ULONG,
17     LONG,
18     FLOAT,
19     DOUBLE,
20     POINTER,
21     STRING,
22   }
23 
24   D_TYPE string_to_dtype(string s) {
25     final switch (s) with (D_TYPE) {
26     case "void":
27       return VOID;
28     case "ubyte":
29       return UBYTE;
30     case "byte":
31       return BYTE;
32     case "ushort":
33       return USHORT;
34     case "short":
35       return SHORT;
36     case "uint":
37       return UINT;
38     case "int":
39       return INT;
40     case "ulong":
41       return ULONG;
42     case "long":
43       return LONG;
44     case "float":
45       return FLOAT;
46     case "double":
47       return DOUBLE;
48     case "pointer":
49       return POINTER;
50     case "string":
51       return STRING;
52     }
53   }
54 
55   ffi_type* d_type_to_ffi_type(D_TYPE type) {
56     return [&ffi_type_void, &ffi_type_uint8, &ffi_type_sint8,
57       &ffi_type_uint16, &ffi_type_sint16, &ffi_type_uint32,
58       &ffi_type_sint32, &ffi_type_uint64, &ffi_type_sint64, &ffi_type_float,
59       &ffi_type_double, &ffi_type_pointer, &ffi_type_pointer][type];
60   }
61 
62   ffi_type*[] d_types_to_ffi_types(D_TYPE[] types) {
63     ffi_type*[] ret;
64     foreach (type; types) {
65       ret ~= d_type_to_ffi_type(type);
66     }
67     return ret;
68   }
69 
70   struct Func {
71     string name;
72     void* ptr;
73     D_TYPE[] arg_types;
74     D_TYPE r_type;
75     ffi_cif cif;
76   }
77 
78   Func newFunc(void* lh, string name, D_TYPE[] arg_types, D_TYPE r_type) {
79     import std..string;
80 
81     void* ptr = dlsym(lh, name.toStringz);
82     char* error = dlerror();
83 
84     if (error) {
85       throw new Error("dlsym error: %s\n".format(error));
86     }
87 
88     ffi_cif cif;
89     ffi_status status;
90 
91     auto _arg_types = d_types_to_ffi_types(arg_types);
92     auto _r_type = d_type_to_ffi_type(r_type);
93 
94     if ((status = ffi_prep_cif(&cif, ffi_abi.FFI_DEFAULT_ABI,
95         arg_types.length.to!uint, _r_type, cast(ffi_type**)_arg_types)) != ffi_status.FFI_OK) {
96       throw new Error("ERROR : %d".format(status));
97     }
98 
99     return Func(name, ptr, arg_types, r_type, cif);
100   }
101 
102   bool checkCastable(T)(void* ptr) {
103     return (cast(T*)ptr) !is null;
104   }
105 
106   Value invokeFunc(Func func, void*[] args) {
107     foreach (i, type; func.arg_types) {
108       final switch (type) with (D_TYPE) {
109       case VOID:
110         break;
111       case UBYTE:
112         if (!checkCastable!(ubyte)(args[i])) {
113           throw new Error("Invalid argument<type error>");
114         }
115         break;
116       case BYTE:
117         if (!checkCastable!(byte)(args[i])) {
118           throw new Error("Invalid argument<type error>");
119         }
120         break;
121       case USHORT:
122         if (!checkCastable!(ushort)(args[i])) {
123           throw new Error("Invalid argument<type error>");
124         }
125         break;
126       case SHORT:
127         if (!checkCastable!(short)(args[i])) {
128           throw new Error("Invalid argument<type error>");
129         }
130         break;
131       case UINT:
132         if (!checkCastable!(uint)(args[i])) {
133           throw new Error("Invalid argument<type error>");
134         }
135         break;
136       case INT:
137         if (!checkCastable!(int)(args[i])) {
138           throw new Error("Invalid argument<type error>");
139         }
140         break;
141       case ULONG:
142         if (!checkCastable!(ulong)(args[i])) {
143           throw new Error("Invalid argument<type error>");
144         }
145         break;
146       case LONG:
147         if (!checkCastable!(long)(args[i])) {
148           throw new Error("Invalid argument<type error>");
149         }
150         break;
151       case FLOAT:
152         if (!checkCastable!(float)(args[i])) {
153           throw new Error("Invalid argument<type error>");
154         }
155         break;
156       case DOUBLE:
157         if (!checkCastable!(double)(args[i])) {
158           throw new Error("Invalid argument<type error>");
159         }
160         break;
161       case POINTER:
162         if (!checkCastable!(void)(args[i])) {
163           throw new Error("Invalid argument<type error>");
164         }
165         break;
166       case STRING:
167         if (!checkCastable!(char)(args[i])) {
168           throw new Error("Invalid argument<type error>");
169         }
170         break;
171       }
172     }
173 
174     ffi_arg result;
175 
176     ffi_call(&func.cif, func.ptr, &result, cast(void**)args);
177 
178     final switch (func.r_type) with (D_TYPE) {
179     case VOID:
180       return new Value;
181     case UBYTE:
182       auto v = cast(ubyte)result;
183       return new Value(v);
184     case BYTE:
185       auto v = cast(byte)result;
186       return new Value(v);
187     case USHORT:
188       auto v = cast(ushort)result;
189       return new Value(v);
190     case SHORT:
191       auto v = cast(short)result;
192       return new Value(v);
193     case UINT:
194       auto v = cast(uint)result;
195       return new Value(v);
196     case INT:
197       auto v = cast(int)result;
198       return new Value(v);
199     case ULONG:
200       auto v = cast(ulong)result;
201       return new Value(v);
202     case LONG:
203       auto v = cast(long)result;
204       return new Value(v);
205     case FLOAT:
206       auto v = cast(float)result;
207       return new Value(v);
208     case DOUBLE:
209       auto v = cast(double)result;
210       return new Value(v);
211     case POINTER:
212       void* ptr = cast(void*)result;
213       return new Value(ptr);
214     case STRING:
215       auto v = cast(char*)result;
216       return new Value(cast(string)v.fromStringz);
217     }
218   }
219 
220   class DllOperator : IOperator {
221     /**
222    * call
223    */
224     void*[] lhs;
225 
226     ~this() {
227       foreach (lh; lhs) {
228         dlclose(lh);
229       }
230     }
231 
232     void* open_dll(string dll_path) {
233       void* lh = dlopen(dll_path.toStringz, RTLD_LAZY);
234       if (!lh) {
235         throw new Error("dlopen error: %s\n".format(dlerror()));
236       }
237       lhs ~= lh;
238       return lh;
239     }
240 
241     public Value call(Engine engine, Value[] args) {
242       struct Func_Info {
243         string name;
244         D_TYPE r_type;
245         D_TYPE[] arg_types;
246       }
247 
248       string dll_path;
249       Func_Info[] func_infos;
250 
251       foreach (arg; args) {
252         if (arg.type == ValueType.Array) {
253           Value[] array = arg.getArray;
254           if (array[0].type != ValueType.SymbolValue) {
255             throw new Error("<1>Invalid Syntax");
256           }
257           if (array[0].getSymbolValue.value == "dll-path") {
258             if (array[1].type != ValueType.String) {
259               throw new Error("<2>Invalid Syntax");
260             }
261             dll_path = array[1].getString;
262           } else if (array[0].getSymbolValue.value == "dll-functions") {
263             if (array[1].type != ValueType.Array) {
264               throw new Error("<3>Invalid Syntax");
265             }
266             Value[] funcs = array[1 .. $];
267 
268             foreach (func; funcs) {
269               if (func.type != ValueType.Array) {
270                 throw new Error("<4>Invalid Syntax");
271               }
272               Value[] info = func.getArray;
273               if (info.length != 3) {
274                 throw new Error("<5>Invalid Syntax");
275               }
276               if (info[0].type != ValueType.String
277                   || info[1].type != ValueType.SymbolValue || info[2].type != ValueType.Array) {
278                 throw new Error("<6>Invalid Syntax");
279               }
280               string name = info[0].getString;
281               D_TYPE r_type = info[1].getSymbolValue.value.string_to_dtype;
282               D_TYPE[] arg_types;
283 
284               foreach (Value type; info[2].getArray) {
285                 if (type.type != ValueType.SymbolValue) {
286                   throw new Error("<7>Invalid Syntax");
287                 }
288                 arg_types ~= type.getSymbolValue.value.string_to_dtype;
289               }
290 
291               func_infos ~= Func_Info(name, r_type, arg_types);
292             }
293           }
294         } else {
295           throw new Error("<dll_op call>Invalid argument");
296         }
297       }
298 
299       if (dll_path.length == 0 || func_infos.length == 0) {
300         throw new Error("Invalid Syntax");
301       }
302 
303       void* lh = open_dll(dll_path);
304 
305       foreach (func_info; func_infos) {
306         IOperator new_op = new class() IOperator {
307           Func func;
308 
309           this() {
310             func = newFunc(lh, func_info.name, func_info.arg_types, func_info.r_type);
311           }
312 
313           ~this() {
314             dlclose(lh);
315           }
316 
317           public Value call(Engine engine, Value[] args) {
318             void*[] f_args;
319             foreach (i, arg_type; func.arg_types) {
320               Value eargs = engine.eval(args[i]);
321 
322               final switch (arg_type) with (D_TYPE) {
323               case VOID:
324                 break;
325               case UBYTE:
326                 if (eargs.type != ValueType.Numeric) {
327                   throw new Error("<ubyte>Invalid Argument");
328                 }
329                 auto arg = eargs.getNumeric.to!(ubyte);
330                 auto ptr = &arg;
331                 f_args ~= ptr;
332                 break;
333               case BYTE:
334                 if (eargs.type != ValueType.Numeric) {
335                   throw new Error("<byte>Invalid Argument");
336                 }
337                 auto arg = eargs.getNumeric.to!(byte);
338                 auto ptr = &arg;
339                 f_args ~= ptr;
340                 break;
341               case USHORT:
342                 if (eargs.type != ValueType.Numeric) {
343                   throw new Error("<ushort>Invalid Argument");
344                 }
345                 auto arg = eargs.getNumeric.to!(ushort);
346                 auto ptr = &arg;
347                 f_args ~= ptr;
348                 break;
349               case SHORT:
350                 if (eargs.type != ValueType.Numeric) {
351                   throw new Error("<short>Invalid Argument");
352                 }
353                 auto arg = eargs.getNumeric.to!(short);
354                 auto ptr = &arg;
355                 f_args ~= ptr;
356                 break;
357               case UINT:
358                 if (eargs.type != ValueType.Numeric) {
359                   throw new Error("<uint>Invalid Argument");
360                 }
361                 auto arg = eargs.getNumeric.to!(uint);
362                 auto ptr = &arg;
363                 f_args ~= ptr;
364                 break;
365               case INT:
366                 if (eargs.type != ValueType.Numeric) {
367                   throw new Error("<int>Invalid Argument");
368                 }
369                 auto arg = eargs.getNumeric.to!(int);
370                 auto ptr = &arg;
371                 f_args ~= ptr;
372                 break;
373               case ULONG:
374                 if (eargs.type != ValueType.Numeric) {
375                   throw new Error("<ulong>Invalid Argument");
376                 }
377                 auto arg = eargs.getNumeric.to!(ulong);
378                 auto ptr = &arg;
379                 f_args ~= ptr;
380                 break;
381               case LONG:
382                 if (eargs.type != ValueType.Numeric) {
383                   throw new Error("<long>Invalid Argument");
384                 }
385                 auto arg = eargs.getNumeric.to!(long);
386                 auto ptr = &arg;
387                 f_args ~= ptr;
388                 break;
389               case FLOAT:
390                 if (eargs.type != ValueType.Numeric) {
391                   throw new Error("<float>Invalid Argument");
392                 }
393                 auto arg = eargs.getNumeric.to!(float);
394                 auto ptr = &arg;
395                 f_args ~= ptr;
396                 break;
397               case DOUBLE:
398                 if (eargs.type != ValueType.Numeric) {
399                   throw new Error("<double>Invalid Argument");
400                 }
401                 auto arg = eargs.getNumeric.to!(double);
402                 auto ptr = &arg;
403                 f_args ~= ptr;
404                 break;
405               case POINTER:
406                 if (eargs.type != ValueType.RAWPointer) {
407                   throw new Error("<ptr>Invalid Argument");
408                 }
409                 auto arg = eargs.getRAWPointer;
410                 void* ptr = &arg;
411                 f_args ~= ptr;
412                 break;
413               case STRING:
414                 if (eargs.type != ValueType.String) {
415                   throw new Error("<string>Invalid Argument");
416                 }
417                 auto arg = eargs.getString.toStringz;
418                 auto ptr = &arg;
419                 f_args ~= ptr;
420                 break;
421               }
422             }
423 
424             return invokeFunc(func, f_args);
425           }
426         };
427 
428         engine.defineVariable(func_info.name, new Value(new_op));
429       }
430 
431       return new Value(true);
432     }
433   }
434 }