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