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 import std.range.primitives; 10 11 import autowrap.csharp.boilerplate; 12 import autowrap.reflection : isModule, PrimordialType; 13 14 enum string methodSetup = " auto attachThread = AttachThread.create();"; 15 16 17 // Wrap global functions from multiple modules 18 public string wrapDLang(Modules...)() if(allSatisfy!(isModule, Modules)) { 19 import autowrap.csharp.common : isDateTimeType, verifySupported; 20 import autowrap.reflection : AllAggregates; 21 22 import std.algorithm.iteration : map; 23 import std.algorithm.sorting : sort; 24 import std.array : array; 25 import std.format : format; 26 import std.meta : AliasSeq; 27 import std.traits : fullyQualifiedName; 28 29 string ret; 30 string[] imports = [Modules].map!(a => a.name)().array(); 31 32 static foreach(T; AliasSeq!(string, wstring, dstring, bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, datetime)) { 33 ret ~= generateSliceMethods!T(imports); 34 } 35 36 static foreach(Agg; AllAggregates!Modules) 37 { 38 static if(verifySupported!Agg && !isDateTimeType!Agg) 39 { 40 ret ~= generateSliceMethods!Agg(imports); 41 ret ~= generateConstructors!Agg(imports); 42 ret ~= generateMethods!Agg(imports); 43 ret ~= generateFields!Agg(imports); 44 } 45 } 46 47 ret ~= generateFunctions!Modules(imports); 48 49 string top = "import autowrap.csharp.boilerplate : AttachThread;" ~ newline; 50 51 foreach(i; sort(imports)) 52 top ~= format("import %s;%s", i, newline); 53 54 return top ~ "\n" ~ ret; 55 } 56 57 // This is to deal with the cases where the parameter name is the same as a 58 // module or pacakage name, which results in errors if the full path name is 59 // used inside the function (e.g. in the return type is prefix.Prefix, and the 60 // parameter is named prefix). 61 private enum AdjParamName(string paramName) = paramName ~ "_param"; 62 63 private string generateConstructors(T)(ref string[] imports) 64 { 65 import autowrap.csharp.common : getDLangInterfaceName, numDefaultArgs, verifySupported; 66 67 import std.algorithm.comparison : among; 68 import std.conv : to; 69 import std.format : format; 70 import std.meta : Filter, staticMap; 71 import std.traits : fullyQualifiedName, hasMember, Parameters, ParameterIdentifierTuple; 72 73 string ret; 74 alias fqn = getDLangInterfaceType!T; 75 76 //Generate constructor methods 77 static if(hasMember!(T, "__ctor") && __traits(getProtection, __traits(getMember, T, "__ctor")).among("export", "public")) 78 { 79 foreach(i, c; __traits(getOverloads, T, "__ctor")) 80 { 81 if (__traits(getProtection, c).among("export", "public")) 82 { 83 alias paramNames = staticMap!(AdjParamName, ParameterIdentifierTuple!c); 84 alias ParamTypes = Parameters!c; 85 86 static if(Filter!(verifySupported, ParamTypes).length != ParamTypes.length) 87 continue; 88 addImports!ParamTypes(imports); 89 90 static foreach(nda; 0 .. numDefaultArgs!c + 1) 91 {{ 92 enum numParams = ParamTypes.length - nda; 93 enum interfaceName = format("%s%s_%s", getDLangInterfaceName(fqn, "__ctor"), i, numParams); 94 95 string exp = "extern(C) export "; 96 exp ~= mixin(interp!"returnValue!(${fqn}) ${interfaceName}("); 97 98 static foreach(pc; 0 .. paramNames.length - nda) 99 exp ~= mixin(interp!"${getDLangInterfaceType!(ParamTypes[pc])} ${paramNames[pc]}, "); 100 101 if (numParams != 0) 102 exp = exp[0 .. $ - 2]; 103 104 exp ~= ") nothrow {" ~ newline; 105 exp ~= " try {" ~ newline; 106 exp ~= methodSetup ~ newline; 107 if (is(T == class)) 108 { 109 exp ~= mixin(interp!" ${fqn} __temp__ = new ${fqn}("); 110 111 static foreach(pc; 0 .. numParams) 112 exp ~= mixin(interp!"${paramNames[pc]}, "); 113 114 if (numParams != 0) 115 exp = exp[0 .. $ - 2]; 116 117 exp ~= ");" ~ newline; 118 exp ~= " pinPointer(cast(void*)__temp__);" ~ newline; 119 exp ~= mixin(interp!" return returnValue!(${fqn})(__temp__);${newline}"); 120 } 121 else if (is(T == struct)) 122 { 123 exp ~= mixin(interp!" return returnValue!(${fqn})(${fqn}("); 124 125 foreach(pn; paramNames) 126 exp ~= mixin(interp!"${pn}, "); 127 128 if (numParams != 0) 129 exp = exp[0 .. $ - 2]; 130 131 exp ~= "));" ~ newline; 132 } 133 134 exp ~= " } catch (Exception __ex__) {" ~ newline; 135 exp ~= mixin(interp!" return returnValue!(${fqn})(__ex__);${newline}"); 136 exp ~= " }" ~ newline; 137 exp ~= "}" ~ newline; 138 ret ~= exp; 139 }} 140 } 141 } 142 } 143 144 return ret; 145 } 146 147 private string generateMethods(T)(ref string[] imports) 148 { 149 import autowrap.csharp.common : isDateTimeType, isDateTimeArrayType, getDLangInterfaceName, 150 numDefaultArgs, verifySupported; 151 152 import std.algorithm.comparison : among; 153 import std.format : format; 154 import std.meta : AliasSeq, Filter, staticMap; 155 import std.traits : isFunction, fullyQualifiedName, ReturnType, Parameters, ParameterIdentifierTuple; 156 157 string ret; 158 alias fqn = getDLangInterfaceType!T; 159 160 foreach(m; __traits(allMembers, T)) 161 { 162 static if (!m.among("__ctor", "toHash", "opEquals", "opCmp", "factory") && 163 is(typeof(__traits(getMember, T, m)))) 164 { 165 foreach(oc, mo; __traits(getOverloads, T, m)) 166 { 167 static if(isFunction!mo && __traits(getProtection, mo).among("export", "public")) 168 { 169 alias RT = ReturnType!mo; 170 alias returnTypeStr = getDLangInterfaceType!RT; 171 alias ParamTypes = Parameters!mo; 172 alias paramNames = staticMap!(AdjParamName, ParameterIdentifierTuple!mo); 173 alias Types = AliasSeq!(RT, ParamTypes); 174 175 static if(Filter!(verifySupported, Types).length != Types.length) 176 continue; 177 else 178 { 179 addImports!Types(imports); 180 181 static foreach(nda; 0 .. numDefaultArgs!mo + 1) 182 {{ 183 enum numParams = ParamTypes.length - nda; 184 enum interfaceName = format("%s%s_%s", getDLangInterfaceName(fqn, m), oc, numParams); 185 186 string exp = "extern(C) export "; 187 188 static if (!is(RT == void)) 189 exp ~= mixin(interp!"returnValue!(${returnTypeStr})"); 190 else 191 exp ~= "returnVoid"; 192 193 exp ~= mixin(interp!" ${interfaceName}("); 194 195 if (is(T == struct)) 196 exp ~= mixin(interp!"ref ${fqn} __obj__, "); 197 else 198 exp ~= mixin(interp!"${fqn} __obj__, "); 199 200 static foreach(pc; 0 .. numParams) 201 exp ~= mixin(interp!"${getDLangInterfaceType!(ParamTypes[pc])} ${paramNames[pc]}, "); 202 203 exp = exp[0 .. $ - 2]; 204 exp ~= ") nothrow {" ~ newline; 205 exp ~= " try {" ~ newline; 206 exp ~= methodSetup ~ newline; 207 exp ~= " "; 208 209 if (!is(RT == void)) 210 exp ~= "auto __result__ = "; 211 212 exp ~= mixin(interp!"__obj__.${m}("); 213 214 static foreach(pc; 0 .. numParams) 215 exp ~= mixin(interp!"${generateParameter!(ParamTypes[pc])(paramNames[pc])}, "); 216 217 if (numParams != 0) 218 exp = exp[0 .. $ - 2]; 219 220 exp ~= ");" ~ newline; 221 222 static if (isDateTimeType!RT || isDateTimeArrayType!RT) 223 exp ~= mixin(interp!" return returnValue!(${returnTypeStr})(${generateReturn!RT(\"__result__\")});${newline}"); 224 else static if (!is(RT == void)) 225 exp ~= mixin(interp!" return returnValue!(${returnTypeStr})(__result__);${newline}"); 226 else 227 exp ~= " return returnVoid();" ~ newline; 228 229 exp ~= " } catch (Exception __ex__) {" ~ newline; 230 231 if (!is(RT == void)) 232 exp ~= mixin(interp!" return returnValue!(${returnTypeStr})(__ex__);${newline}"); 233 else 234 exp ~= " return returnVoid(__ex__);" ~ newline; 235 236 exp ~= " }" ~ newline; 237 exp ~= "}" ~ newline; 238 ret ~= exp; 239 }} 240 } 241 } 242 } 243 } 244 } 245 246 return ret; 247 } 248 249 private string generateFields(T)(ref string[] imports) { 250 import autowrap.csharp.common : getDLangInterfaceName, verifySupported; 251 252 import std.traits : fullyQualifiedName, Fields, FieldNameTuple; 253 import std.algorithm: among; 254 255 string ret; 256 alias fqn = getDLangInterfaceType!T; 257 if (is(T == class) || is(T == interface)) 258 { 259 alias FieldTypes = Fields!T; 260 alias fieldNames = FieldNameTuple!T; 261 static foreach(fc; 0 .. FieldTypes.length) 262 {{ 263 alias FT = FieldTypes[fc]; 264 static if(verifySupported!FT && __traits(getProtection, __traits(getMember,T,fieldNames[fc])).among("export", "public")) 265 { 266 alias fn = fieldNames[fc]; 267 static if (is(typeof(__traits(getMember, T, fn)))) 268 { 269 addImport!FT(imports); 270 271 ret ~= mixin(interp!"extern(C) export returnValue!(${getDLangInterfaceType!FT}) ${getDLangInterfaceName(fqn, fn ~ \"_get\")}(${fqn} __obj__) nothrow {${newline}"); 272 ret ~= generateMethodErrorHandling(mixin(interp!" auto __value__ = __obj__.${fn};${newline} return returnValue!(${getDLangInterfaceType!FT})(${generateReturn!FT(\"__value__\")});"), mixin(interp!"returnValue!(${getDLangInterfaceType!FT})")); 273 ret ~= "}" ~ newline; 274 ret ~= mixin(interp!"extern(C) export returnVoid ${getDLangInterfaceName(fqn, fn ~ \"_set\")}(${fqn} __obj__, ${getDLangInterfaceType!FT} value) nothrow {${newline}"); 275 ret ~= generateMethodErrorHandling(mixin(interp!" __obj__.${fn} = ${generateParameter!FT(\"value\")};${newline} return returnVoid();"), "returnVoid"); 276 ret ~= "}" ~ newline; 277 } 278 } 279 }} 280 } 281 return ret; 282 } 283 284 private string generateFunctions(Modules...)(ref string[] imports) 285 if(allSatisfy!(isModule, Modules)) 286 { 287 import autowrap.csharp.common : getDLangInterfaceName, numDefaultArgs, verifySupported; 288 import autowrap.reflection: AllFunctions; 289 290 import std.format : format; 291 import std.meta : AliasSeq, Filter, staticMap; 292 import std.traits : fullyQualifiedName, hasMember, functionAttributes, FunctionAttribute, 293 ReturnType, Parameters, ParameterIdentifierTuple; 294 295 string ret; 296 297 foreach(func; AllFunctions!Modules) 298 { 299 foreach(oc, overload; __traits(getOverloads, func.module_, func.name)) 300 { 301 alias RT = ReturnType!overload; 302 alias ParamTypes = Parameters!overload; 303 alias Types = AliasSeq!(RT, ParamTypes); 304 305 static if(Filter!(verifySupported, Types).length != Types.length) 306 continue; 307 else 308 { 309 addImports!Types(imports); 310 311 static foreach(nda; 0 .. numDefaultArgs!overload + 1) 312 {{ 313 enum numParams = ParamTypes.length - nda; 314 enum interfaceName = format("%s%s_%s", getDLangInterfaceName(func.moduleName, null, func.name), oc, numParams); 315 alias returnTypeStr = getDLangInterfaceType!RT; 316 alias paramNames = staticMap!(AdjParamName, ParameterIdentifierTuple!overload); 317 318 static if (!is(RT == void)) 319 string retType = mixin(interp!"returnValue!(${returnTypeStr})"); 320 else 321 string retType = "returnVoid"; 322 323 string funcStr = "extern(C) export "; 324 funcStr ~= mixin(interp!"${retType} ${interfaceName}("); 325 326 static foreach(pc; 0 .. numParams) 327 funcStr ~= mixin(interp!"${getDLangInterfaceType!(ParamTypes[pc])} ${paramNames[pc]}, "); 328 329 if(numParams != 0) 330 funcStr = funcStr[0 .. $ - 2]; 331 332 funcStr ~= ") nothrow {" ~ newline; 333 funcStr ~= " try {" ~ newline; 334 funcStr ~= methodSetup ~ newline; 335 funcStr ~= " "; 336 337 if (!is(RT == void)) 338 funcStr ~= mixin(interp!"auto __return__ = ${func.name}("); 339 else 340 funcStr ~= mixin(interp!"${func.name}("); 341 342 static foreach(pc; 0 .. numParams) 343 funcStr ~= mixin(interp!"${generateParameter!(ParamTypes[pc])(paramNames[pc])}, "); 344 345 if(numParams != 0) 346 funcStr = funcStr[0 .. $ - 2]; 347 348 funcStr ~= ");" ~ newline; 349 350 if (!is(RT == void)) 351 funcStr ~= mixin(interp!" return ${retType}(${generateReturn!RT(\"__return__\")});${newline}"); 352 else 353 funcStr ~= mixin(interp!" return ${retType}();${newline}"); 354 355 funcStr ~= " } catch (Exception __ex__) {" ~ newline; 356 funcStr ~= mixin(interp!" return ${retType}(__ex__);${newline}"); 357 funcStr ~= " }" ~ newline; 358 funcStr ~= "}" ~ newline; 359 360 ret ~= funcStr; 361 }} 362 } 363 } 364 } 365 366 return ret; 367 } 368 369 private string generateSliceMethods(T)(ref string[] imports) { 370 import autowrap.csharp.common : getDLangSliceInterfaceName; 371 372 import std.traits : fullyQualifiedName, moduleName, TemplateOf; 373 374 addImport!T(imports); 375 376 alias fqn = getDLangInterfaceType!T; 377 378 //Generate slice creation method 379 string ret = mixin(interp!"extern(C) export returnValue!(${fqn}[]) ${getDLangSliceInterfaceName(fqn, \"Create\")}(size_t capacity) nothrow {${newline}"); 380 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}[])")); 381 ret ~= "}" ~ newline; 382 383 //Generate slice method 384 ret ~= mixin(interp!"extern(C) export returnValue!(${fqn}[]) ${getDLangSliceInterfaceName(fqn, \"Slice\")}(${fqn}[] slice, size_t begin, size_t end) nothrow {${newline}"); 385 ret ~= generateMethodErrorHandling(mixin(interp!" ${fqn}[] __temp__ = slice[begin..end];${newline} pinPointer(cast(void*)__temp__.ptr);${newline} return returnValue!(${fqn}[])(__temp__);"), mixin(interp!"returnValue!(${fqn}[])")); 386 ret ~= "}" ~ newline; 387 388 //Generate get method 389 ret ~= mixin(interp!"extern(C) export returnValue!(${fqn}) ${getDLangSliceInterfaceName(fqn, \"Get\")}(${fqn}[] slice, size_t index) nothrow {${newline}"); 390 ret ~= generateMethodErrorHandling(mixin(interp!" return returnValue!(${fqn})(slice[index]);"), mixin(interp!"returnValue!(${fqn})")); 391 ret ~= "}" ~ newline; 392 393 //Generate set method 394 ret ~= mixin(interp!"extern(C) export returnVoid ${getDLangSliceInterfaceName(fqn, \"Set\")}(${fqn}[] slice, size_t index, ${fqn} set) nothrow {${newline}"); 395 ret ~= generateMethodErrorHandling(mixin(interp!" slice[index] = set;${newline} return returnVoid();"), "returnVoid"); 396 ret ~= "}" ~ newline; 397 398 //Generate item append method 399 ret ~= mixin(interp!"extern(C) export returnValue!(${fqn}[]) ${getDLangSliceInterfaceName(fqn, \"AppendValue\")}(${fqn}[] slice, ${fqn} append) nothrow {${newline}"); 400 ret ~= generateMethodErrorHandling(mixin(interp!" return returnValue!(${fqn}[])(slice ~= append);"), mixin(interp!"returnValue!(${fqn}[])")); 401 ret ~= "}" ~ newline; 402 403 //Generate slice append method 404 ret ~= mixin(interp!"extern(C) export returnValue!(${fqn}[]) ${getDLangSliceInterfaceName(fqn, \"AppendSlice\")}(${fqn}[] slice, ${fqn}[] append) nothrow {${newline}"); 405 ret ~= generateMethodErrorHandling(mixin(interp!" return returnValue!(${fqn}[])(slice ~= append);"), mixin(interp!"returnValue!(${fqn}[])")); 406 ret ~= "}" ~ newline; 407 408 return ret; 409 } 410 411 private string generateMethodErrorHandling(string insideCode, string returnType) { 412 string ret = " try {" ~ newline; 413 ret ~= methodSetup ~ newline; 414 ret ~= insideCode ~ newline; 415 ret ~= " } catch (Exception __ex__) {" ~ newline; 416 ret ~= mixin(interp!" return ${returnType}(__ex__);${newline}"); 417 ret ~= " }" ~ newline; 418 return ret; 419 } 420 421 private string generateParameter(T)(string name) { 422 import autowrap.csharp.common : isDateTimeType, isDateTimeArrayType; 423 import std.datetime : DateTime, Date, TimeOfDay, SysTime, Duration; 424 import std.traits : fullyQualifiedName; 425 426 alias fqn = fullyQualifiedName!(PrimordialType!T); 427 static if (isDateTimeType!T) { 428 return mixin(interp!"fromDatetime!(${fqn})(${name})"); 429 } else static if (isDateTimeArrayType!T) { 430 return mixin(interp!"fromDatetime1DArray!(${fqn})(${name})"); 431 } else { 432 return name; 433 } 434 } 435 436 private string generateReturn(T)(string name) { 437 import autowrap.csharp.common : isDateTimeType, isDateTimeArrayType; 438 import std.datetime : DateTime, Date, TimeOfDay, SysTime, Duration; 439 import std.traits : fullyQualifiedName; 440 441 alias fqn = fullyQualifiedName!(PrimordialType!T); 442 static if (isDateTimeType!T) { 443 return mixin(interp!"toDatetime!(${fqn})(${name})"); 444 } else static if (isDateTimeArrayType!T) { 445 return mixin(interp!"toDatetime1DArray!(${fqn})(${name})"); 446 } else { 447 return name; 448 } 449 } 450 451 private void addImports(T...)(ref string[] imports) 452 { 453 foreach(U; T) 454 addImport!U(imports); 455 } 456 457 private void addImport(T)(ref string[] imports) 458 { 459 import std.algorithm.searching : canFind; 460 import std.traits : isBuiltinType, isDynamicArray, moduleName; 461 import autowrap.csharp.common : isSupportedType; 462 463 static assert(isSupportedType!T, "missing check for supported type"); 464 465 static if(isDynamicArray!T) 466 addImport!(ElementType!T)(imports); 467 else static if(!isBuiltinType!T) 468 { 469 enum mod = moduleName!T; 470 if(!mod.empty && !imports.canFind(mod)) 471 imports ~= mod; 472 } 473 } 474 475 private string getDLangInterfaceType(T)() { 476 import autowrap.csharp.common : isDateTimeArrayType, isDateTimeType; 477 import std.traits : fullyQualifiedName; 478 if (isDateTimeType!T) { 479 return "datetime"; 480 } else if (isDateTimeArrayType!T) { 481 return "datetime[]"; 482 } else { 483 return fullyQualifiedName!T; 484 } 485 }