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