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