1 module autowrap.csharp.dlang;
2 
3 import autowrap.reflection : isModule;
4 import std.ascii : newline;
5 import std.meta : allSatisfy;
6 
7 enum string methodSetup = "        thread_attachThis();
8         rt_moduleTlsCtor();
9         scope(exit) rt_moduleTlsDtor();
10         scope(exit) thread_detachThis();";
11 
12 // Wrap global functions from multiple modules
13 public string wrapDLang(Modules...)() if(allSatisfy!(isModule, Modules)) {
14     import autowrap.csharp.common : getDLangInterfaceName;
15     import autowrap.reflection : AllAggregates;
16     import std.traits : fullyQualifiedName, moduleName;
17     import std.meta : AliasSeq;
18 
19     string ret = string.init;
20     ret ~= "import core.thread : thread_attachThis, thread_detachThis;" ~ newline;
21     ret ~= "extern(C) void rt_moduleTlsCtor();" ~ newline;
22     ret ~= "extern(C) void rt_moduleTlsDtor();" ~ newline;
23     foreach(mod; Modules) {
24         ret ~= "import " ~ mod.name ~ ";" ~ newline;
25     }
26     ret ~= newline;
27 
28     static foreach(t; AliasSeq!(string, wstring, dstring, bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double)) {
29         ret ~= generateSliceMethods!t();
30     }
31 
32     foreach(agg; AllAggregates!Modules) {
33         alias modName = moduleName!agg;
34         alias fqn = fullyQualifiedName!agg;
35 
36         ret ~= generateSliceMethods!agg();
37 
38         ret ~= generateConstructors!agg();
39 
40         ret ~= generateMethods!agg();
41 
42         ret ~= generateFields!agg();
43     }
44 
45     ret ~= generateFunctions!Modules();
46 
47     return ret;
48 }
49 
50 private string generateConstructors(T)() {
51     import autowrap.csharp.common : getDLangInterfaceName;
52     import std.traits : fullyQualifiedName, hasMember, Parameters, ParameterIdentifierTuple;
53     import std.meta : AliasSeq;
54 
55     string ret = string.init;
56     alias fqn = fullyQualifiedName!T;
57     static if(hasMember!(T, "__ctor")) {
58         alias constructors = AliasSeq!(__traits(getOverloads, T, "__ctor"));
59     } else {
60         alias constructors = AliasSeq!();
61     }
62 
63     //Generate constructor methods
64     foreach(c; constructors) {
65         alias paramNames = ParameterIdentifierTuple!c;
66         alias paramTypes = Parameters!c;
67         string exp = "extern(C) export ";
68         const string interfaceName = getDLangInterfaceName(fqn, "__ctor");
69 
70         exp ~= "returnValue!(" ~ fqn ~ ")";
71         exp ~= " " ~ interfaceName ~ "(";
72 
73         static foreach(pc; 0..paramNames.length) {
74             exp ~= fullyQualifiedName!(paramTypes[pc]) ~ " " ~ paramNames[pc] ~ ", ";
75         }
76         if (paramTypes.length > 0) {
77             exp = exp[0..$-2];
78         }
79         exp ~= ") nothrow {" ~ newline;
80         exp ~= "    try {" ~ newline;
81         exp ~= methodSetup ~ newline;
82         if (is(T == class)) {
83             exp ~= "        import std.stdio : writeln;" ~ newline;
84             exp ~= "        " ~ fqn ~ " __temp__ = new " ~ fqn ~ "(";
85             static foreach(pc; 0..paramNames.length) {
86                 exp ~= paramNames[pc] ~ ", ";
87             }
88             if (paramTypes.length > 0) {
89                 exp = exp[0..$-2];
90             }
91             exp ~= ");" ~ newline;
92             exp ~= "        pinPointer(cast(void*)__temp__);" ~ newline;
93             exp ~= "        return returnValue!(" ~ fqn ~ ")(__temp__);" ~ newline;
94         } else if (is(T == struct)) {
95             exp ~= "        return " ~ fqn ~ "(";
96             foreach(pn; paramNames) {
97                 exp ~= pn ~ ", ";
98             }
99             if (paramTypes.length > 0) {
100                 exp = exp[0..$-2];
101             }
102             exp ~= ");" ~ newline;
103         }
104         exp ~= "    } catch (Exception __ex__) {" ~ newline;
105         exp ~= "        return returnValue!(" ~ fqn ~ ")(__ex__);" ~ newline;
106         exp ~= "    }" ~ newline;
107         exp ~= "}" ~ newline;
108         ret ~= exp;
109     }
110 
111     return ret;
112 }
113 
114 private string generateMethods(T)() {
115     import autowrap.csharp.common : getDLangInterfaceName;
116     import std.traits : isFunction, fullyQualifiedName, ReturnType, Parameters, ParameterIdentifierTuple;
117     import std.conv : to;
118 
119     string ret = string.init;
120     alias fqn = fullyQualifiedName!T;
121     foreach(m; __traits(allMembers, T)) {
122         if (m == "__ctor" || m == "toHash" || m == "opEquals" || m == "opCmp" || m == "factory") {
123             continue;
124         }
125 
126         static if (is(typeof(__traits(getMember, T, m)))) {
127             foreach(oc, mo; __traits(getOverloads, T, m)) {
128                 const bool isMethod = isFunction!mo;
129 
130                 static if(isMethod) {
131                     string exp = string.init;
132                     const string interfaceName = getDLangInterfaceName(fqn, m);
133 
134                     alias returnType = ReturnType!mo;
135                     alias returnTypeStr = fullyQualifiedName!returnType;
136                     alias paramTypes = Parameters!mo;
137                     alias paramNames = ParameterIdentifierTuple!mo;
138 
139                     exp ~= "extern(C) export ";
140                     if (!is(returnType == void)) {
141                         exp ~= "returnValue!(" ~ returnTypeStr ~ ")";
142                     } else {
143                         exp ~= "returnVoid";
144                     }
145                     exp ~= " " ~ interfaceName ~ to!string(oc) ~ "(";
146                     if (is(T == struct)) {
147                         exp ~= "ref " ~ fqn ~ " __obj__, ";
148                     } else {
149                         exp ~= fqn ~ " __obj__, ";
150                     }
151                     static foreach(pc; 0..paramNames.length) {
152                         exp ~= fullyQualifiedName!(paramTypes[pc]) ~ " " ~ paramNames[pc] ~ ", ";
153                     }
154                     exp = exp[0..$-2];
155                     exp ~= ") nothrow {" ~ newline;
156                     exp ~= "    try {" ~ newline;
157                     exp ~= methodSetup ~ newline;
158                     exp ~= "        ";
159                     if (!is(returnType == void)) {
160                         exp ~= "auto __result__ = ";
161                     }
162                     exp ~= "__obj__." ~ m ~ "(";
163                     static foreach(pc; 0..paramNames.length) {
164                         exp ~= paramNames[pc] ~ ", ";
165                     }
166                     if (paramNames.length > 0) {
167                         exp = exp[0..$-2];
168                     }
169                     exp ~= ");" ~ newline;
170                     if (!is(returnType == void)) {
171                         exp ~= "        return returnValue!(" ~ returnTypeStr ~ ")(__result__);" ~ newline;
172                     } else {
173                         exp ~= "        return returnVoid();" ~ newline;
174                     }
175                     exp ~= "    } catch (Exception __ex__) {" ~ newline;
176                     if (!is(returnType == void)) {
177                         exp ~= "        return returnValue!(" ~ returnTypeStr ~ ")(__ex__);" ~ newline;
178                     } else {
179                         exp ~= "        return returnVoid(__ex__);" ~ newline;
180                     }
181                     exp ~= "    }" ~ newline;
182                     exp ~= "}" ~ newline;
183                     ret ~= exp;
184                 }
185             }
186         }
187     }
188 
189     return ret;
190 }
191 
192 private string generateFields(T)() {
193     import autowrap.csharp.common : getDLangInterfaceName;
194     import std.traits : fullyQualifiedName, Fields, FieldNameTuple;
195 
196     string ret = string.init;
197     alias fqn = fullyQualifiedName!T;
198     if (is(T == class) || is(T == interface)) {
199         alias fieldTypes = Fields!T;
200         alias fieldNames = FieldNameTuple!T;
201         static foreach(fc; 0..fieldTypes.length) {
202             static if (is(typeof(__traits(getMember, T, fieldNames[fc])))) {
203                 ret ~= "extern(C) export returnValue!(" ~ fullyQualifiedName!(fieldTypes[fc]) ~ ") " ~ getDLangInterfaceName(fqn, fieldNames[fc] ~ "_get") ~ "(" ~ fqn ~ " __obj__) nothrow {" ~ newline;
204                 ret ~= generateMethodErrorHandling("        return returnValue!(" ~ fullyQualifiedName!(fieldTypes[fc]) ~ ")(__obj__." ~ fieldNames[fc] ~ ");", "returnValue!(" ~ fullyQualifiedName!(fieldTypes[fc]) ~ ")");
205                 ret ~= "}" ~ newline;
206                 ret ~= "extern(C) export returnVoid " ~ getDLangInterfaceName(fqn, fieldNames[fc] ~ "_set") ~ "(" ~ fqn ~ " __obj__, " ~ fullyQualifiedName!(fieldTypes[fc]) ~ " value) nothrow {" ~ newline;
207                 ret ~= generateMethodErrorHandling("        __obj__." ~ fieldNames[fc] ~ " = value;" ~ newline ~ "        return returnVoid();", "returnVoid");
208                 ret ~= "}" ~ newline;
209             }
210         }
211     }
212     return ret;
213 }
214 
215 private string generateFunctions(Modules...)() if(allSatisfy!(isModule, Modules)) {
216     import autowrap.csharp.common : getDLangInterfaceName;
217     import autowrap.reflection: AllFunctions;
218     import std.traits : fullyQualifiedName, hasMember, functionAttributes, FunctionAttribute, ReturnType, Parameters, ParameterIdentifierTuple;
219 
220     string ret = string.init;
221     foreach(func; AllFunctions!Modules) {
222         alias modName = func.moduleName;
223         alias funcName = func.name;
224 
225         alias returnType = ReturnType!(__traits(getMember, func.module_, func.name));
226         alias returnTypeStr = fullyQualifiedName!(ReturnType!(__traits(getMember, func.module_, func.name)));
227         alias paramTypes = Parameters!(__traits(getMember, func.module_, func.name));
228         alias paramNames = ParameterIdentifierTuple!(__traits(getMember, func.module_, func.name));
229         const string interfaceName = getDLangInterfaceName(modName, null, funcName);
230         string retType = string.init;
231         string funcStr = "extern(C) export ";
232 
233         if (!is(returnType == void)) {
234             retType = retType ~ "returnValue!(" ~ returnTypeStr ~ ")";
235         } else {
236             retType = retType ~ "returnVoid";
237         }
238 
239         funcStr ~= retType ~ " " ~ interfaceName ~ "(";
240         static foreach(pc; 0..paramNames.length) {
241             funcStr ~= fullyQualifiedName!(paramTypes[pc]) ~ " " ~ paramNames[pc] ~ ", ";
242         }
243         if(paramNames.length > 0) {
244             funcStr = funcStr[0..$-2];
245         }
246         funcStr ~= ") nothrow {" ~ newline;
247         funcStr ~= "    try {" ~ newline;
248         funcStr ~= methodSetup ~ newline;
249         if (!is(returnType == void)) {
250             funcStr ~= "        return " ~ retType ~ "(" ~ func.name ~ "(";
251             foreach(pName; paramNames) {
252                 funcStr ~= pName ~ ", ";
253             }
254             if(paramNames.length > 0) {
255                 funcStr = funcStr[0..$-2];
256             }
257             funcStr ~= "));" ~ newline;
258         } else {
259             funcStr ~= func.name ~ "(";
260             static foreach(pc; 0..paramNames.length) {
261                 funcStr ~= paramNames[pc] ~ ", ";
262             }
263             if(paramNames.length > 0) {
264                 funcStr = funcStr[0..$-2];
265             }
266             funcStr ~= ");" ~ newline;
267             funcStr ~= "        return returnVoid();" ~ newline;
268         }
269         funcStr ~= "    } catch (Exception __ex__) {" ~ newline;
270         funcStr ~= "        return " ~ retType ~ "(__ex__);" ~ newline;
271         funcStr ~= "    }" ~ newline;
272         funcStr ~= "}" ~ newline;
273 
274         ret ~= funcStr;
275     }
276 
277     return ret;
278 }
279 
280 private string generateSliceMethods(T)() {
281     import autowrap.csharp.common : getDLangSliceInterfaceName;
282     import std.traits : fullyQualifiedName;
283     string ret = string.init;
284     alias fqn = fullyQualifiedName!T;
285 
286     //Generate slice creation method
287     ret ~= "extern(C) export returnValue!(" ~ fqn ~ "[]) " ~ getDLangSliceInterfaceName(fqn, "Create") ~ "(size_t capacity) nothrow {" ~ newline;
288     ret ~= generateMethodErrorHandling("        " ~ fqn ~ "[] __temp__;
289         __temp__.reserve(capacity);
290         pinPointer(cast(void*)__temp__.ptr);
291         return returnValue!(" ~ fqn ~ "[])(__temp__);", "returnValue!(" ~ fqn ~ "[])");
292     ret ~= "}" ~ newline;
293 
294     //Generate slice method
295     ret ~= "extern(C) export returnValue!(" ~ fqn ~ "[]) " ~ getDLangSliceInterfaceName(fqn, "Slice") ~ "(" ~ fqn ~ "[] slice, size_t begin, size_t end) nothrow {" ~ newline;
296     ret ~= generateMethodErrorHandling("        " ~ fqn ~ "[] __temp__ = slice[begin..end];
297         pinPointer(cast(void*)__temp__.ptr);
298         return returnValue!(" ~ fqn ~ "[])(__temp__);", "returnValue!(" ~ fqn ~ "[])");
299     ret ~= "}" ~ newline;
300 
301     //Generate get method
302     ret ~= "extern(C) export returnValue!(" ~ fqn ~ ") " ~ getDLangSliceInterfaceName(fqn, "Get") ~ "(" ~ fqn ~ "[] slice, size_t index) nothrow {" ~ newline;
303     ret ~= generateMethodErrorHandling("        return returnValue!(" ~ fqn ~ ")(slice[index]);", "returnValue!(" ~ fqn ~ ")");
304     ret ~= "}" ~ newline;
305 
306     //Generate set method
307     ret ~= "extern(C) export returnVoid " ~ getDLangSliceInterfaceName(fqn, "Set") ~ "(" ~ fqn ~ "[] slice, size_t index, " ~ fqn ~ " set) nothrow {" ~ newline;
308     ret ~= generateMethodErrorHandling("        slice[index] = set;" ~ newline ~ "        return returnVoid();", "returnVoid");
309     ret ~= "}" ~ newline;
310 
311     //Generate item append method
312     ret ~= "extern(C) export returnValue!(" ~ fqn ~ "[]) " ~ getDLangSliceInterfaceName(fqn, "AppendValue") ~ "(" ~ fqn ~ "[] slice, " ~ fqn ~ " append) nothrow {" ~ newline;
313     ret ~= generateMethodErrorHandling("        return returnValue!(" ~ fqn ~ "[])(slice ~= append);", "returnValue!(" ~ fqn ~ "[])");
314     ret ~= "}" ~ newline;
315 
316     //Generate slice append method
317     ret ~= "extern(C) export returnValue!(" ~ fqn ~ "[]) " ~ getDLangSliceInterfaceName(fqn, "AppendSlice") ~ "(" ~ fqn ~ "[] slice, " ~ fqn ~ "[] append) nothrow {" ~ newline;
318     ret ~= generateMethodErrorHandling("        return returnValue!(" ~ fqn ~ "[])(slice ~= append);", "returnValue!(" ~ fqn ~ "[])");
319     ret ~= "}" ~ newline;
320 
321     return ret;
322 }
323 
324 private string generateMethodErrorHandling(string insideCode, string returnType) {
325     string ret = string.init;
326     ret ~= "    try {" ~ newline;
327     ret ~= methodSetup ~ newline;
328     ret ~= insideCode ~ newline;
329     ret ~= "    } catch (Exception __ex__) {" ~ newline;
330     ret ~= "        return " ~ returnType ~ "(__ex__);" ~ newline;
331     ret ~= "    }" ~ newline;
332     return ret;
333 }