1 /** 2 * Internal reflection templates for implementing mocking behaviour. 3 * 4 * License: 5 * MIT. See LICENSE for full details. 6 */ 7 module dunit.reflection; 8 9 /** 10 * Imports. 11 */ 12 import dunit.toolkit; 13 import std.array; 14 import std.range; 15 import std..string; 16 import std.traits; 17 18 /** 19 * Generate a string containing the protection level of the passed function. 20 * 21 * Params: 22 * func = The function to inspect. 23 */ 24 private template MethodProtection(func...) if (func.length == 1 && isCallable!(func)) 25 { 26 enum MethodProtection = __traits(getProtection, func); 27 } 28 29 unittest 30 { 31 class T 32 { 33 public void method1(){} 34 protected void method2(){} 35 private void method3(){} 36 } 37 38 MethodProtection!(T.method1).assertEqual("public"); 39 MethodProtection!(T.method2).assertEqual("protected"); 40 MethodProtection!(T.method3).assertEqual("private"); 41 } 42 43 /** 44 * Generate a string containing the synchronization of the passed function. 45 * 46 * Params: 47 * func = The function to inspect. 48 */ 49 private template MethodSynchonization(func...) if (func.length == 1 && isCallable!(func)) 50 { 51 enum MethodSynchonization = isMethodShared!(func) ? "shared" : ""; 52 } 53 54 unittest 55 { 56 class T 57 { 58 public shared void method1(){} 59 public synchronized void method2(){} 60 public void method3(){} 61 } 62 63 MethodSynchonization!(T.method1).assertEqual("shared"); 64 MethodSynchonization!(T.method2).assertEqual("shared"); 65 MethodSynchonization!(T.method3).assertEqual(""); 66 } 67 68 /** 69 * Generate a string containing the attributes of the passed function. 70 * 71 * Params: 72 * func = The function to inspect. 73 */ 74 private template MethodAttributes(func...) if (func.length == 1 && isCallable!(func)) 75 { 76 private string getMethodAttributes() 77 { 78 string code = ""; 79 80 with (FunctionAttribute) 81 { 82 static if (functionAttributes!(func) & property) 83 { 84 code ~= "@property "; 85 } 86 87 static if (functionAttributes!(func) & trusted) 88 { 89 code ~= "@trusted "; 90 } 91 92 static if (functionAttributes!(func) & safe) 93 { 94 code ~= "@safe "; 95 } 96 97 static if (functionAttributes!(func) & pure_) 98 { 99 code ~= "pure "; 100 } 101 102 static if (functionAttributes!(func) & nothrow_) 103 { 104 code ~= "nothrow "; 105 } 106 107 static if (functionAttributes!(func) & ref_) 108 { 109 code ~= "ref "; 110 } 111 } 112 return code.stripRight(); 113 } 114 enum MethodAttributes = getMethodAttributes(); 115 } 116 117 unittest 118 { 119 class T 120 { 121 public @property @trusted void method1(){} 122 public @safe pure nothrow void method2(){} 123 public ref int method3(ref int foo){return foo;} 124 } 125 126 MethodAttributes!(T.method1).assertEqual("@property @trusted"); 127 MethodAttributes!(T.method2).assertEqual("@safe pure nothrow"); 128 MethodAttributes!(T.method3).assertEqual("ref"); 129 } 130 131 /** 132 * Generate a string containing the return type of the passed function. 133 * 134 * Params: 135 * func = The function to inspect. 136 */ 137 private template MethodReturnType(func...) if (func.length == 1 && isCallable!(func)) 138 { 139 enum MethodReturnType = ReturnType!(func).stringof; 140 } 141 142 unittest 143 { 144 class T 145 { 146 public int method1(){return 1;} 147 public string method2(){return "foo";} 148 public char method3(){return 'a';} 149 } 150 151 MethodReturnType!(T.method1).assertEqual("int"); 152 MethodReturnType!(T.method2).assertEqual("string"); 153 MethodReturnType!(T.method3).assertEqual("char"); 154 } 155 156 /** 157 * Generate a string containing the name of the passed function. 158 * 159 * Params: 160 * func = The function to inspect. 161 */ 162 private template MethodName(func...) if (func.length == 1 && isCallable!(func)) 163 { 164 enum MethodName = __traits(identifier, func); 165 } 166 167 unittest 168 { 169 class T 170 { 171 public void method1(){} 172 public void method2(){} 173 public void method3(){} 174 } 175 176 MethodName!(T.method1).assertEqual("method1"); 177 MethodName!(T.method2).assertEqual("method2"); 178 MethodName!(T.method3).assertEqual("method3"); 179 } 180 181 /** 182 * Generate a string array containing the storage classes of each parameter (if any) of the passed function. 183 * 184 * Params: 185 * func = The function to inspect. 186 */ 187 private template MethodParameterStorageClasses(func...) if (func.length == 1 && isCallable!(func)) 188 { 189 private string[] getMethodParameterStorageClasses() 190 { 191 string[] storageClasses; 192 string code; 193 194 foreach (storageClass; ParameterStorageClassTuple!(func)) 195 { 196 code = ""; 197 198 static if (storageClass == ParameterStorageClass.scope_) 199 { 200 code ~= "scope "; 201 } 202 203 static if (storageClass == ParameterStorageClass.lazy_) 204 { 205 code ~= "lazy "; 206 } 207 208 static if (storageClass == ParameterStorageClass.out_) 209 { 210 code ~= "out "; 211 } 212 213 static if (storageClass == ParameterStorageClass.ref_) 214 { 215 code ~= "ref "; 216 } 217 218 storageClasses ~= code; 219 } 220 221 return storageClasses; 222 } 223 enum MethodParameterStorageClasses = getMethodParameterStorageClasses(); 224 } 225 226 unittest 227 { 228 class T 229 { 230 public void method1(scope int* foo){} 231 public void method2(lazy int bar){} 232 public void method3(out int baz){} 233 public void method4(ref int qux){} 234 } 235 236 MethodParameterStorageClasses!(T.method1).assertEqual(["scope "]); 237 MethodParameterStorageClasses!(T.method2).assertEqual(["lazy "]); 238 MethodParameterStorageClasses!(T.method3).assertEqual(["out "]); 239 MethodParameterStorageClasses!(T.method4).assertEqual(["ref "]); 240 } 241 242 /** 243 * Generate a string array containing the parameter types of the passed function. 244 * 245 * Params: 246 * func = The function to inspect. 247 */ 248 private template MethodParameterTypes(func...) if (func.length == 1 && isCallable!(func)) 249 { 250 private string[] getMethodParameterTypes() 251 { 252 string[] types; 253 254 foreach (type; ParameterTypeTuple!(func)) 255 { 256 types ~= type.stringof; 257 } 258 259 return types; 260 } 261 enum MethodParameterTypes = getMethodParameterTypes(); 262 } 263 264 unittest 265 { 266 class T 267 { 268 public void method1(const int foo){} 269 public void method2(string bar){} 270 public void method3(bool baz){} 271 } 272 273 MethodParameterTypes!(T.method1).assertEqual(["const(int)"]); 274 MethodParameterTypes!(T.method2).assertEqual(["string"]); 275 MethodParameterTypes!(T.method3).assertEqual(["bool"]); 276 } 277 278 /** 279 * Generate a string array containing the parameter identifiers of the passed function. 280 * 281 * Params: 282 * func = The function to inspect. 283 */ 284 private template MethodParameterIdentifiers(func...) if (func.length == 1 && isCallable!(func)) 285 { 286 private string[] getMethodParameterIdentifiers() 287 { 288 string[] names; 289 290 foreach (name; ParameterIdentifierTuple!(func)) 291 { 292 names ~= name; 293 } 294 295 return names; 296 } 297 enum MethodParameterIdentifiers = getMethodParameterIdentifiers(); 298 } 299 300 unittest 301 { 302 class T 303 { 304 public void method1(const int foo){} 305 public void method2(string bar){} 306 public void method3(bool baz){} 307 } 308 309 MethodParameterIdentifiers!(T.method1).assertEqual(["foo"]); 310 MethodParameterIdentifiers!(T.method2).assertEqual(["bar"]); 311 MethodParameterIdentifiers!(T.method3).assertEqual(["baz"]); 312 } 313 314 /** 315 * Generate a string array containing the default values of each parameter (if any) of the passed function. 316 * 317 * Params: 318 * func = The function to inspect. 319 */ 320 private template MethodParameterDefaultValues(func...) if (func.length == 1 && isCallable!(func)) 321 { 322 private string[] getMethodParameterDefaultValues() 323 { 324 string[] defaultValues; 325 326 foreach (defaultValue; ParameterDefaultValueTuple!(func)) 327 { 328 static if (is(defaultValue == void)) 329 { 330 defaultValues ~= ""; 331 } 332 else 333 { 334 defaultValues ~= " = " ~ defaultValue.stringof; 335 } 336 } 337 338 return defaultValues; 339 } 340 enum MethodParameterDefaultValues = getMethodParameterDefaultValues(); 341 } 342 343 unittest 344 { 345 class T 346 { 347 public void method1(const int foo){} 348 public void method2(string bar = "qux"){} 349 public void method3(bool baz = true){} 350 } 351 352 MethodParameterDefaultValues!(T.method1).assertEqual([""]); 353 MethodParameterDefaultValues!(T.method2).assertEqual([" = \"qux\""]); 354 MethodParameterDefaultValues!(T.method3).assertEqual([" = true"]); 355 } 356 357 /** 358 * Generate a string containing the full parameter signature of the passed function. 359 * 360 * Params: 361 * func = The function to inspect. 362 */ 363 private template MethodParameters(func...) if (func.length == 1 && isCallable!(func)) 364 { 365 private string getMethodParameters() 366 { 367 string[] storageClasses = MethodParameterStorageClasses!(func); 368 string[] types = MethodParameterTypes!(func); 369 string[] names = MethodParameterIdentifiers!(func); 370 string[] defaultValues = MethodParameterDefaultValues!(func); 371 372 string[] parameters; 373 374 foreach (storageClass, type, name, defaultValue; zip(storageClasses, types, names, defaultValues)) 375 { 376 parameters ~= format("%s%s %s%s", storageClass, type, name, defaultValue); 377 } 378 379 return format("(%s)", parameters.join(", ")); 380 } 381 enum MethodParameters = getMethodParameters(); 382 } 383 384 unittest 385 { 386 class T 387 { 388 public void method1(const int foo){} 389 public void method2(string bar = "qux"){} 390 public void method3(bool baz = true){} 391 } 392 393 MethodParameters!(T.method1).assertEqual("(const(int) foo)"); 394 MethodParameters!(T.method2).assertEqual("(string bar = \"qux\")"); 395 MethodParameters!(T.method3).assertEqual("(bool baz = true)"); 396 } 397 398 /** 399 * Generate a string containing the mangled name of the passed function. 400 * 401 * Params: 402 * func = The function to inspect. 403 */ 404 private template MethodMangledName(func...) if (func.length == 1 && isCallable!(func)) 405 { 406 enum MethodMangledName = mangledName!(func); 407 } 408 409 /** 410 * Returns true if the passed function is nothrow, false if not. 411 * 412 * Params: 413 * func = The function to inspect. 414 */ 415 private template isMethodNoThrow(func...) if (func.length == 1 && isCallable!(func)) 416 { 417 enum isMethodNoThrow = cast(bool)(functionAttributes!(func) & FunctionAttribute.nothrow_); 418 } 419 420 unittest 421 { 422 class T 423 { 424 public void method1() nothrow {} 425 public void method2() {} 426 } 427 428 isMethodNoThrow!(T.method1).assertTrue(); 429 isMethodNoThrow!(T.method2).assertFalse(); 430 } 431 432 /** 433 * Returns true if the passed function is const, false if not. 434 * 435 * Params: 436 * func = The function to inspect. 437 */ 438 private template isMethodConst(func...) if (func.length == 1 && isCallable!(func)) 439 { 440 enum isMethodConst = !isMutable!(FunctionTypeOf!(func)); 441 } 442 443 unittest 444 { 445 class T 446 { 447 public void method1() const {} 448 public void method2() immutable {} 449 public void method3(){} 450 } 451 452 isMethodConst!(T.method1).assertTrue(); 453 isMethodConst!(T.method2).assertTrue(); 454 isMethodConst!(T.method3).assertFalse(); 455 } 456 457 /** 458 * Returns true if the passed function is shared or synchronized, false if not. 459 * 460 * Params: 461 * func = The function to inspect. 462 */ 463 private template isMethodShared(func...) if (func.length == 1 && isCallable!(func)) 464 { 465 enum isMethodShared = is(shared(FunctionTypeOf!(func)) == FunctionTypeOf!(func)); 466 } 467 468 unittest 469 { 470 static class T 471 { 472 public synchronized void method1() {} 473 public shared void method2(int value) {} 474 public shared void method3(int value) const pure nothrow @safe @property {} 475 public shared shared(T) method4() { return null; } 476 public shared(T) method5() { return null; } 477 public void method6() {} 478 } 479 480 isMethodShared!(T.method1).assertTrue(); 481 isMethodShared!(T.method2).assertTrue(); 482 isMethodShared!(T.method3).assertTrue(); 483 isMethodShared!(T.method4).assertTrue(); 484 isMethodShared!(T.method5).assertFalse(); 485 isMethodShared!(T.method6).assertFalse(); 486 } 487 488 /** 489 * Generate a string containing the body of the passed function. 490 * 491 * Params: 492 * hasParent = true if this function is replacing a parent implementation. 493 * func = The function to inspect and generate code for. 494 */ 495 private template MethodBody(bool hasParent, func...) 496 { 497 private string getMethodBody() 498 { 499 string code = ""; 500 code ~= "\ttry\n"; 501 code ~= "\t{\n"; 502 503 static if (!isMethodConst!(func)) 504 { 505 code ~= "\t\tif (\"" ~ MethodSignature!(func) ~ "\" in this._methodCount)\n"; 506 code ~= "\t\t{\n"; 507 code ~= "\t\t\tthis._methodCount[\"" ~ MethodSignature!(func) ~ "\"].actual++;\n"; 508 code ~= "\t\t}\n"; 509 } 510 511 code ~= "\t\tif (this." ~ MethodMangledName!(func) ~ ")\n"; 512 code ~= "\t\t{\n"; 513 code ~= "\t\t\treturn this." ~ MethodMangledName!(func) ~ "(" ~ MethodParameterIdentifiers!(func).join(", ") ~ ");\n"; 514 code ~= "\t\t}\n"; 515 code ~= "\t\telse\n"; 516 code ~= "\t\t{\n"; 517 518 static if (hasParent) 519 { 520 code ~= "\t\t\tif (this._useParentMethods)\n"; 521 code ~= "\t\t\t{\n"; 522 code ~= "\t\t\t\treturn super." ~ MethodName!(func) ~ "(" ~ MethodParameterIdentifiers!(func).join(", ") ~ ");\n"; 523 code ~= "\t\t\t}\n"; 524 code ~= "\t\t\telse\n"; 525 code ~= "\t\t\t{\n"; 526 code ~= "\t\t\t\tauto error = new DUnitAssertError(\"Mock method not implemented\", this._disableMethodsLocation.file, this._disableMethodsLocation.line);\n"; 527 code ~= "\t\t\t\terror.addInfo(\"Method\", this.className ~ \"." ~ MethodSignature!(func) ~ "\");\n"; 528 code ~= "\t\t\t\tthrow error;\n"; 529 code ~= "\t\t\t}\n"; 530 } 531 else 532 { 533 code ~= "\t\t\t\tauto error = new DUnitAssertError(\"Mock method not implemented\", this._disableMethodsLocation.file, this._disableMethodsLocation.line);\n"; 534 code ~= "\t\t\t\terror.addInfo(\"Method\", this.className ~ \"." ~ MethodSignature!(func) ~ "\");\n"; 535 code ~= "\t\t\t\tthrow error;\n"; 536 } 537 538 code ~= "\t\t}\n"; 539 code ~= "\t}\n"; 540 code ~= "\tcatch(Exception ex)\n"; 541 code ~= "\t{\n"; 542 543 static if (isMethodNoThrow!(func)) 544 { 545 code ~= "\t\tassert(false, ex.msg);\n"; 546 } 547 else 548 { 549 code ~= "\t\tthrow ex;\n"; 550 } 551 552 code ~= "\t}\n"; 553 code ~= "\tassert(false, \"Critical error occurred!\");\n"; 554 return code; 555 } 556 enum MethodBody = getMethodBody(); 557 } 558 559 /** 560 * Generate a string containing the code for a delegate property. 561 * 562 * Params: 563 * func = The function to inspect. 564 */ 565 private template MethodDelegateProperty(func...) if (func.length == 1 && isCallable!(func)) 566 { 567 private string getMethodDelegateProperty() 568 { 569 return format("private %s %s;\n", MethodDelegateSignature!(func), MethodMangledName!(func)); 570 } 571 enum MethodDelegateProperty = getMethodDelegateProperty(); 572 } 573 574 /** 575 * Generate a string containing the entire code for the passed method. 576 * 577 * Params: 578 * hasParent = true if this function is replacing a parent implementation. 579 * func = The function to inspect and generate code for. 580 */ 581 private template Method(bool hasParent, func...) if (func.length == 1 && isCallable!(func)) 582 { 583 private string getMethod() 584 { 585 string code = ""; 586 587 static if (hasParent) 588 { 589 code ~= "override "; 590 } 591 592 code ~= MethodProtection!(func) ~ " "; 593 code ~= MethodAttributes!(func) ~ " "; 594 code ~= MethodReturnType!(func) ~ " "; 595 code ~= MethodName!(func); 596 code ~= MethodParameters!(func) ~ (isMethodConst!(func) ? " const \n" : "\n"); 597 code ~= "{\n"; 598 code ~= MethodBody!(hasParent, func); 599 code ~= "}\n"; 600 return code; 601 } 602 enum Method = getMethod(); 603 } 604 605 /** 606 * Iterate through the methods of T generating code using the generator. 607 * 608 * Params: 609 * T = The class to inspect. 610 * generator = The template to use to generate code for each method. 611 * index = The beginning index of the members. 612 */ 613 public template DUnitMethodIterator(T, string generator, int index = 0) if (is(T == class) || is(T == interface)) 614 { 615 private string getResult() 616 { 617 string code = ""; 618 static if (index < __traits(allMembers, T).length) 619 { 620 static if (MemberFunctionsTuple!(T, __traits(allMembers, T)[index]).length) 621 { 622 foreach (func; __traits(getVirtualMethods, T, __traits(allMembers, T)[index])) 623 { 624 static if (!__traits(isFinalFunction, func)) 625 { 626 mixin("code ~= " ~ generator ~ ";"); 627 } 628 } 629 } 630 code ~= DUnitMethodIterator!(T, generator, index + 1); 631 } 632 return code; 633 } 634 enum DUnitMethodIterator = getResult(); 635 } 636 637 /** 638 * Generate a string containing the entire override code for the passed constructor. 639 * 640 * Params: 641 * T = The class to inspect. 642 * func = The function to inspect. 643 */ 644 private template Constructor(T, func...) if (is(T == class) && func.length == 1 && isCallable!(func)) 645 { 646 private string getConstructor() 647 { 648 string code = ""; 649 code ~= "this"; 650 code ~= MethodParameters!(func) ~ "\n"; 651 code ~= "{\n"; 652 code ~= "\tsuper(" ~ MethodParameterIdentifiers!(func).join(", ") ~ ");\n"; 653 code ~= "}\n"; 654 return code; 655 } 656 enum Constructor = getConstructor(); 657 } 658 659 unittest 660 { 661 class T 662 { 663 this(int foo, int bar) 664 { 665 } 666 } 667 668 string code = "this(int foo, int bar) 669 { 670 super(foo, bar); 671 }\n"; 672 673 Constructor!(T, T.__ctor).assertEqual(code); 674 } 675 676 /** 677 * Iterate through the constructors of T generating code using the generator. 678 * 679 * Params: 680 * T = The class to inspect. 681 * generator = The template to use to generate code for each constructor. 682 */ 683 public template DUnitConstructorIterator(T, string generator) if (is(T == class)) 684 { 685 private string getResult() 686 { 687 string code = ""; 688 static if (__traits(hasMember, T, "__ctor")) 689 { 690 foreach (func; __traits(getOverloads, T, "__ctor")) 691 { 692 mixin("code ~= " ~ generator ~ ";"); 693 } 694 } 695 return code; 696 } 697 enum DUnitConstructorIterator = getResult(); 698 } 699 700 unittest 701 { 702 class A 703 { 704 this(){} 705 this(int foo, int bar) 706 { 707 } 708 } 709 710 class B {} 711 712 string code = "this() 713 { 714 super(); 715 } 716 this(int foo, int bar) 717 { 718 super(foo, bar); 719 }\n"; 720 721 DUnitConstructorIterator!(A, "Constructor!(T, func)").assertEqual(code); 722 DUnitConstructorIterator!(B, "Constructor!(T, func)").assertEqual(""); 723 } 724 725 /** 726 * Generate a string containing the parameter signature of the passed function. 727 * 728 * Params: 729 * func = The function to inspect. 730 */ 731 private template MethodParameterSignature(func...) if (func.length == 1 && isCallable!(func)) 732 { 733 private string getMethodParameterSignature() 734 { 735 string[] storageClasses = MethodParameterStorageClasses!(func); 736 string[] types = MethodParameterTypes!(func); 737 string[] parameters; 738 739 foreach (storageClass, type; zip(storageClasses, types)) 740 { 741 parameters ~= format("%s%s", storageClass, type); 742 } 743 744 return parameters.join(", "); 745 } 746 enum MethodParameterSignature = getMethodParameterSignature(); 747 } 748 749 unittest 750 { 751 class T 752 { 753 public void method1(const int foo, string bar){} 754 public void method2(string baz, bool qux){} 755 public void method3(ref char quux){} 756 } 757 758 MethodParameterSignature!(T.method1).assertEqual("const(int), string"); 759 MethodParameterSignature!(T.method2).assertEqual("string, bool"); 760 MethodParameterSignature!(T.method3).assertEqual("ref char"); 761 } 762 763 /** 764 * Generate a string containing the signature of the passed function. 765 * 766 * Params: 767 * func = The function to inspect. 768 */ 769 private template MethodSignature(func...) if (func.length == 1 && isCallable!(func)) 770 { 771 private string getMethodSignature() 772 { 773 return format("%s:%s(%s)", MethodReturnType!(func), MethodName!(func), MethodParameterSignature!(func)); 774 } 775 enum MethodSignature = getMethodSignature(); 776 } 777 778 unittest 779 { 780 class T 781 { 782 public int method1(const int foo, string bar){return 1;} 783 public void method2(string baz, bool qux){} 784 public bool method3(ref char quux){return true;} 785 } 786 787 MethodSignature!(T.method1).assertEqual("int:method1(const(int), string)"); 788 MethodSignature!(T.method2).assertEqual("void:method2(string, bool)"); 789 MethodSignature!(T.method3).assertEqual("bool:method3(ref char)"); 790 } 791 792 /** 793 * Generate a string containing the delegate signature of the passed function. 794 * 795 * Bugs: 796 * The 'ref' attribute is not supported in the delegate signature due to a compiler bug. 797 * Once this bug is fixed we can enable it. http://d.puremagic.com/issues/show_bug.cgi?id=5050 798 * 799 * Params: 800 * func = The function to inspect. 801 */ 802 private template MethodDelegateSignature(func...) if (func.length == 1 && isCallable!(func)) 803 { 804 private string getMethodDelegateSignature() 805 { 806 return format("%s delegate(%s) %s", MethodReturnType!(func), MethodParameterSignature!(func), MethodAttributes!(func)) 807 .replace(" ref", "") 808 .stripRight(); 809 } 810 enum MethodDelegateSignature = getMethodDelegateSignature(); 811 } 812 813 unittest 814 { 815 interface T 816 { 817 public void method1(const int foo, string bar) @safe pure nothrow; 818 public void method2(string baz, bool qux); 819 public void method3(ref char quux); 820 public ref int method4(string bux); 821 } 822 823 MethodDelegateSignature!(T.method1).assertEqual("void delegate(const(int), string) @safe pure nothrow"); 824 MethodDelegateSignature!(T.method2).assertEqual("void delegate(string, bool)"); 825 MethodDelegateSignature!(T.method3).assertEqual("void delegate(ref char)"); 826 MethodDelegateSignature!(T.method4).assertEqual("int delegate(string)"); 827 } 828 829 /** 830 * Generate a string containing the signature switch. 831 * 832 * Params: 833 * func = The function to inspect. 834 */ 835 private template MethodSignatureSwitch(func...) if (func.length == 1 && isCallable!(func)) 836 { 837 private string getMethodSignatureSwitch() 838 { 839 string code = ""; 840 code ~= "case \"" ~ MethodSignature!(func) ~ "\":\n"; 841 code ~= "\tthis." ~ MethodMangledName!(func) ~ " = cast(" ~ MethodDelegateSignature!(func) ~ ")delegate_;\n"; 842 code ~= "\tbreak;\n"; 843 return code; 844 } 845 enum MethodSignatureSwitch = getMethodSignatureSwitch(); 846 }