1 /** 2 * Written in the D programming language. 3 * Module initialization routines. 4 * 5 * Copyright: Copyright Digital Mars 2000 - 2013. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * (See accompanying file LICENSE) 9 * Authors: Walter Bright, Sean Kelly 10 * Source: $(DRUNTIMESRC rt/_minfo.d) 11 */ 12 13 module rt.minfo; 14 15 import core.stdc.stdlib; // alloca 16 import core.stdc.string; // memcpy 17 import rt.sections; 18 19 enum 20 { 21 MIctorstart = 0x1, // we've started constructing it 22 MIctordone = 0x2, // finished construction 23 MIstandalone = 0x4, // module ctor does not depend on other module 24 // ctors being done first 25 MItlsctor = 8, 26 MItlsdtor = 0x10, 27 MIctor = 0x20, 28 MIdtor = 0x40, 29 MIxgetMembers = 0x80, 30 MIictor = 0x100, 31 MIunitTest = 0x200, 32 MIimportedModules = 0x400, 33 MIlocalClasses = 0x800, 34 MIname = 0x1000, 35 } 36 37 /***** 38 * A ModuleGroup is an unordered collection of modules. 39 * There is exactly one for: 40 * 1. all statically linked in D modules, either directely or as shared libraries 41 * 2. each call to rt_loadLibrary() 42 */ 43 44 struct ModuleGroup 45 { 46 this(immutable(ModuleInfo*)[] modules) nothrow @nogc 47 { 48 _modules = modules; 49 } 50 51 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc 52 { 53 return _modules; 54 } 55 56 // this function initializes the bookeeping necessary to create the 57 // cycle path, and then creates it. It is a precondition that src and 58 // target modules are involved in a cycle. 59 // 60 // The return value is malloc'd using C, so it must be freed after use. 61 private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges) nothrow 62 { 63 import core.bitop : bt, btc, bts; 64 65 // set up all the arrays. 66 size_t[] cyclePath = (cast(size_t*)malloc(size_t.sizeof * _modules.length * 2))[0 .. _modules.length * 2]; 67 size_t totalMods; 68 int[] distance = (cast(int*)malloc(int.sizeof * _modules.length))[0 .. _modules.length]; 69 scope(exit) 70 .free(distance.ptr); 71 72 // determine the shortest path between two modules. Uses dijkstra 73 // without a priority queue. (we can be a bit slow here, in order to 74 // get a better printout). 75 void shortest(size_t start, size_t target) 76 { 77 // initial setup 78 distance[] = int.max; 79 int curdist = 0; 80 distance[start] = 0; 81 while (true) 82 { 83 bool done = true; 84 foreach (i, x; distance) 85 { 86 if (x == curdist) 87 { 88 if (i == target) 89 { 90 done = true; 91 break; 92 } 93 foreach (n; edges[i]) 94 { 95 if (distance[n] == int.max) 96 { 97 distance[n] = curdist + 1; 98 done = false; 99 } 100 } 101 } 102 } 103 if (done) 104 break; 105 ++curdist; 106 } 107 // it should be impossible to not get to target, this is just a 108 // sanity check. Not an assert, because druntime is compiled in 109 // release mode. 110 if (distance[target] != curdist) 111 { 112 assert(0, "internal error printing module cycle"); 113 } 114 115 // determine the path. This is tricky, because we have to 116 // follow the edges in reverse to get back to the original. We 117 // don't have a reverse mapping, so it takes a bit of looping. 118 totalMods += curdist; 119 auto subpath = cyclePath[totalMods - curdist .. totalMods]; 120 while (true) 121 { 122 --curdist; 123 subpath[curdist] = target; 124 if (curdist == 0) 125 break; 126 distloop: 127 // search for next (previous) module in cycle. 128 foreach (m, d; distance) 129 { 130 if (d == curdist) 131 { 132 // determine if m can reach target 133 foreach (e; edges[m]) 134 { 135 if (e == target) 136 { 137 // recurse 138 target = m; 139 break distloop; 140 } 141 } 142 } 143 } 144 } 145 } 146 147 // first get to the target 148 shortest(srcidx, targetidx); 149 // now get back. 150 shortest(targetidx, srcidx); 151 152 return cyclePath[0 .. totalMods]; 153 } 154 155 /****************************** 156 * Allocate and fill in _ctors[] and _tlsctors[]. 157 * Modules are inserted into the arrays in the order in which the constructors 158 * need to be run. 159 * 160 * Params: 161 * cycleHandling - string indicating option for cycle handling 162 * Throws: 163 * Exception if it fails. 164 */ 165 void sortCtors(string cycleHandling) nothrow 166 { 167 import core.bitop : bts, btr, bt, BitRange; 168 import core.internal.container.hashtab; 169 170 enum OnCycle 171 { 172 abort, 173 print, 174 ignore 175 } 176 177 auto onCycle = OnCycle.abort; 178 179 switch (cycleHandling) with(OnCycle) 180 { 181 case "deprecate": 182 import core.stdc.stdio : fprintf, stderr; 183 // Option deprecated in 2.101, remove in 2.111 184 fprintf(stderr, "`--DRT-oncycle=deprecate` is no longer supported, using `abort` instead\n"); 185 break; 186 case "abort": 187 onCycle = abort; 188 break; 189 case "print": 190 onCycle = print; 191 break; 192 case "ignore": 193 onCycle = ignore; 194 break; 195 case "": 196 // no option passed 197 break; 198 default: 199 // invalid cycle handling option. 200 assert(0, "DRT invalid cycle handling option: " ~ cycleHandling); 201 } 202 203 debug (printModuleDependencies) 204 { 205 import core.stdc.stdio : printf; 206 207 foreach (_m; _modules) 208 { 209 printf("%s%s%s:", _m.name.ptr, (_m.flags & MIstandalone) 210 ? "+".ptr : "".ptr, (_m.flags & (MIctor | MIdtor)) ? "*".ptr : "".ptr); 211 foreach (_i; _m.importedModules) 212 printf(" %s", _i.name.ptr); 213 printf("\n"); 214 } 215 } 216 217 immutable uint len = cast(uint) _modules.length; 218 if (!len) 219 return; // nothing to do. 220 221 // allocate some stack arrays that will be used throughout the process. 222 immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); 223 immutable flagbytes = nwords * size_t.sizeof; 224 auto ctorstart = cast(size_t*) malloc(flagbytes); // ctor/dtor seen 225 auto ctordone = cast(size_t*) malloc(flagbytes); // ctor/dtor processed 226 auto relevant = cast(size_t*) malloc(flagbytes); // has ctors/dtors 227 scope (exit) 228 { 229 .free(ctorstart); 230 .free(ctordone); 231 .free(relevant); 232 } 233 234 void clearFlags(size_t* flags) 235 { 236 memset(flags, 0, flagbytes); 237 } 238 239 240 // build the edges between each module. We may need this for printing, 241 // and also allows avoiding keeping a hash around for module lookups. 242 int[][] edges = (cast(int[]*)malloc((int[]).sizeof * _modules.length))[0 .. _modules.length]; 243 { 244 HashTab!(immutable(ModuleInfo)*, int) modIndexes; 245 foreach (i, m; _modules) 246 modIndexes[m] = cast(int) i; 247 248 auto reachable = cast(size_t*) malloc(flagbytes); 249 scope(exit) 250 .free(reachable); 251 252 foreach (i, m; _modules) 253 { 254 // use bit array to prevent duplicates 255 // https://issues.dlang.org/show_bug.cgi?id=16208 256 clearFlags(reachable); 257 // preallocate enough space to store all the indexes 258 int *edge = cast(int*)malloc(int.sizeof * _modules.length); 259 size_t nEdges = 0; 260 foreach (imp; m.importedModules) 261 { 262 if (imp is m) // self-import 263 continue; 264 if (auto impidx = imp in modIndexes) 265 { 266 if (!bts(reachable, *impidx)) 267 edge[nEdges++] = *impidx; 268 } 269 } 270 if (nEdges > 0) 271 { 272 // trim space to what is needed 273 edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges]; 274 } 275 else 276 { 277 edges[i] = null; 278 .free(edge); 279 } 280 } 281 } 282 283 // free all the edges after we are done 284 scope(exit) 285 { 286 foreach (e; edges) 287 if (e.ptr) 288 .free(e.ptr); 289 .free(edges.ptr); 290 } 291 292 void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink) 293 { 294 version (Windows) 295 enum EOL = "\r\n"; 296 else 297 enum EOL = "\n"; 298 299 sink("Cyclic dependency between module constructors/destructors of "); 300 sink(_modules[sourceIdx].name); 301 sink(" and "); 302 sink(_modules[cycleIdx].name); 303 sink(EOL); 304 auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges); 305 scope(exit) .free(cyclePath.ptr); 306 307 sink(_modules[sourceIdx].name); 308 sink("* ->" ~ EOL); 309 foreach (x; cyclePath[0 .. $ - 1]) 310 { 311 sink(_modules[x].name); 312 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL); 313 } 314 sink(_modules[sourceIdx].name); 315 sink("*" ~ EOL); 316 } 317 318 // find all the non-trivial dependencies (that is, dependencies that have a 319 // ctor or dtor) of a given module. Doing this, we can 'skip over' the 320 // trivial modules to get at the non-trivial ones. 321 // 322 // If a cycle is detected, returns the index of the module that completes the cycle. 323 // Returns: true for success, false for a deprecated cycle error 324 bool findDeps(size_t idx, size_t* reachable) nothrow 325 { 326 static struct stackFrame 327 { 328 size_t curMod; 329 size_t curDep; 330 } 331 332 // initialize "stack" 333 auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len); 334 scope (exit) 335 .free(stack); 336 auto stacktop = stack + len; 337 auto sp = stack; 338 sp.curMod = cast(int) idx; 339 sp.curDep = 0; 340 341 // initialize reachable by flagging source module 342 clearFlags(reachable); 343 bts(reachable, idx); 344 345 for (;;) 346 { 347 auto m = _modules[sp.curMod]; 348 if (sp.curDep >= edges[sp.curMod].length) 349 { 350 // return 351 if (sp == stack) // finished the algorithm 352 break; 353 --sp; 354 } 355 else 356 { 357 auto midx = edges[sp.curMod][sp.curDep]; 358 if (!bts(reachable, midx)) 359 { 360 if (bt(relevant, midx)) 361 { 362 // need to process this node, don't recurse. 363 if (bt(ctorstart, midx)) 364 { 365 // was already started, this is a cycle. 366 final switch (onCycle) with(OnCycle) 367 { 368 case abort: 369 370 string errmsg = ""; 371 buildCycleMessage(idx, midx, (string x) {errmsg ~= x;}); 372 throw new Error(errmsg, __FILE__, __LINE__); 373 case ignore: 374 break; 375 case print: 376 // print the message 377 buildCycleMessage(idx, midx, (string x) { 378 import core.stdc.stdio : fprintf, stderr; 379 fprintf(stderr, "%.*s", cast(int) x.length, x.ptr); 380 }); 381 // continue on as if this is correct. 382 break; 383 } 384 } 385 } 386 else if (!bt(ctordone, midx)) 387 { 388 // non-relevant, and hasn't been exhaustively processed, recurse. 389 if (++sp >= stacktop) 390 { 391 // stack overflow, this shouldn't happen. 392 import core.internal.abort : abort; 393 394 abort("stack overflow on dependency search"); 395 } 396 sp.curMod = midx; 397 sp.curDep = 0; 398 continue; 399 } 400 } 401 } 402 403 // next dependency 404 ++sp.curDep; 405 } 406 return true; // success 407 } 408 409 // The list of constructors that will be returned by the sorting. 410 immutable(ModuleInfo)** ctors; 411 // current element being inserted into ctors list. 412 size_t ctoridx = 0; 413 414 // This function will determine the order of construction/destruction and 415 // check for cycles. If a cycle is found, the cycle path is transformed 416 // into a string and thrown as an error. 417 // 418 // Each call into this function is given a module that has static 419 // ctor/dtors that must be dealt with. It recurses only when it finds 420 // dependencies that also have static ctor/dtors. 421 // Returns: true for success, false for a deprecated cycle error 422 bool processMod(size_t curidx) nothrow 423 { 424 immutable ModuleInfo* current = _modules[curidx]; 425 426 // First, determine what modules are reachable. 427 auto reachable = cast(size_t*) malloc(flagbytes); 428 scope (exit) 429 .free(reachable); 430 if (!findDeps(curidx, reachable)) 431 return false; // deprecated cycle error 432 433 // process the dependencies. First, we process all relevant ones 434 bts(ctorstart, curidx); 435 auto brange = BitRange(reachable, len); 436 foreach (i; brange) 437 { 438 // note, don't check for cycles here, because the config could have been set to ignore cycles. 439 // however, don't recurse if there is one, so still check for started ctor. 440 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i)) 441 { 442 if (!processMod(i)) 443 return false; // deprecated cycle error 444 } 445 } 446 447 // now mark this node, and all nodes reachable from this module as done. 448 bts(ctordone, curidx); 449 btr(ctorstart, curidx); 450 foreach (i; brange) 451 { 452 // Since relevant dependencies are already marked as done 453 // from recursion above (or are going to be handled up the call 454 // stack), no reason to check for relevance, that is a wasted 455 // op. 456 bts(ctordone, i); 457 } 458 459 // add this module to the construction order list 460 ctors[ctoridx++] = current; 461 return true; 462 } 463 464 // returns `false` if deprecated cycle error otherwise set `result`. 465 bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow 466 { 467 clearFlags(relevant); 468 clearFlags(ctorstart); 469 clearFlags(ctordone); 470 471 // pre-allocate enough space to hold all modules. 472 ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof)); 473 ctoridx = 0; 474 foreach (idx, m; _modules) 475 { 476 if (m.flags & relevantFlags) 477 { 478 if (m.flags & MIstandalone) 479 { 480 // can run at any time. Just run it first. 481 ctors[ctoridx++] = m; 482 } 483 else 484 { 485 bts(relevant, idx); 486 } 487 } 488 } 489 490 // now run the algorithm in the relevant ones 491 foreach (idx; BitRange(relevant, len)) 492 { 493 if (!bt(ctordone, idx)) 494 { 495 if (!processMod(idx)) 496 return false; 497 } 498 } 499 500 if (ctoridx == 0) 501 { 502 // no ctors in the list. 503 .free(ctors); 504 } 505 else 506 { 507 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof); 508 if (ctors is null) 509 assert(0); 510 result = ctors[0 .. ctoridx]; 511 } 512 return true; 513 } 514 515 // finally, do the sorting for both shared and tls ctors. If either returns false, 516 // print the deprecation warning. 517 if (!doSort(MIctor | MIdtor, _ctors) || 518 !doSort(MItlsctor | MItlsdtor, _tlsctors)) 519 { 520 // print a warning 521 import core.stdc.stdio : fprintf, stderr; 522 fprintf(stderr, "Deprecation 16211 warning:\n" 523 ~ "A cycle has been detected in your program that was undetected prior to DMD\n" 524 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n" 525 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n"); 526 527 } 528 } 529 530 /// ditto 531 void sortCtors() 532 { 533 import rt.config : rt_configOption; 534 sortCtors(rt_configOption("oncycle")); 535 } 536 537 void runCtors() 538 { 539 // run independent ctors 540 runModuleFuncs!(m => m.ictor)(_modules); 541 // sorted module ctors 542 runModuleFuncs!(m => m.ctor)(_ctors); 543 } 544 545 void runTlsCtors() 546 { 547 runModuleFuncs!(m => m.tlsctor)(_tlsctors); 548 } 549 550 void runTlsDtors() 551 { 552 runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors); 553 } 554 555 void runDtors() 556 { 557 runModuleFuncsRev!(m => m.dtor)(_ctors); 558 } 559 560 void free() 561 { 562 if (_ctors.ptr) 563 .free(_ctors.ptr); 564 _ctors = null; 565 if (_tlsctors.ptr) 566 .free(_tlsctors.ptr); 567 _tlsctors = null; 568 // _modules = null; // let the owner free it 569 } 570 571 private: 572 immutable(ModuleInfo*)[] _modules; 573 immutable(ModuleInfo)*[] _ctors; 574 immutable(ModuleInfo)*[] _tlsctors; 575 } 576 577 578 /******************************************** 579 * Iterate over all module infos. 580 */ 581 582 int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg) 583 { 584 foreach (ref sg; SectionGroup) 585 { 586 foreach (m; sg.modules) 587 { 588 // TODO: Should null ModuleInfo be allowed? 589 if (m !is null) 590 { 591 if (auto res = dg(m)) 592 return res; 593 } 594 } 595 } 596 return 0; 597 } 598 599 /******************************************** 600 * Module constructor and destructor routines. 601 */ 602 603 extern (C) 604 { 605 void rt_moduleCtor() 606 { 607 foreach (ref sg; SectionGroup) 608 { 609 sg.moduleGroup.sortCtors(); 610 sg.moduleGroup.runCtors(); 611 } 612 } 613 614 void rt_moduleTlsCtor() 615 { 616 foreach (ref sg; SectionGroup) 617 { 618 sg.moduleGroup.runTlsCtors(); 619 } 620 } 621 622 void rt_moduleTlsDtor() 623 { 624 foreach_reverse (ref sg; SectionGroup) 625 { 626 sg.moduleGroup.runTlsDtors(); 627 } 628 } 629 630 void rt_moduleDtor() 631 { 632 foreach_reverse (ref sg; SectionGroup) 633 { 634 sg.moduleGroup.runDtors(); 635 sg.moduleGroup.free(); 636 } 637 } 638 639 version (Win32) 640 { 641 // Alternate names for backwards compatibility with older DLL code 642 void _moduleCtor() 643 { 644 rt_moduleCtor(); 645 } 646 647 void _moduleDtor() 648 { 649 rt_moduleDtor(); 650 } 651 652 void _moduleTlsCtor() 653 { 654 rt_moduleTlsCtor(); 655 } 656 657 void _moduleTlsDtor() 658 { 659 rt_moduleTlsDtor(); 660 } 661 } 662 } 663 664 /******************************************** 665 */ 666 667 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 668 { 669 foreach (m; modules) 670 { 671 if (auto fp = getfp(m)) 672 (*fp)(); 673 } 674 } 675 676 void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 677 { 678 foreach_reverse (m; modules) 679 { 680 if (auto fp = getfp(m)) 681 (*fp)(); 682 } 683 } 684 685 unittest 686 { 687 static void assertThrown(T : Throwable, E)(lazy E expr, string msg) 688 { 689 try 690 expr; 691 catch (T) 692 return; 693 assert(0, msg); 694 } 695 696 static void stub() 697 { 698 } 699 700 static struct UTModuleInfo 701 { 702 this(uint flags) 703 { 704 mi._flags = flags; 705 } 706 707 void setImports(immutable(ModuleInfo)*[] imports...) 708 { 709 import core.bitop; 710 assert(flags & MIimportedModules); 711 712 immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor)); 713 immutable size = nfuncs * (void function()).sizeof + 714 size_t.sizeof + imports.length * (ModuleInfo*).sizeof; 715 assert(size <= pad.sizeof); 716 717 pad[nfuncs] = imports.length; 718 .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof); 719 } 720 721 immutable ModuleInfo mi; 722 size_t[8] pad; 723 alias mi this; 724 } 725 726 static UTModuleInfo mockMI(uint flags) 727 { 728 auto mi = UTModuleInfo(flags | MIimportedModules); 729 auto p = cast(void function()*)&mi.pad; 730 if (flags & MItlsctor) *p++ = &stub; 731 if (flags & MItlsdtor) *p++ = &stub; 732 if (flags & MIctor) *p++ = &stub; 733 if (flags & MIdtor) *p++ = &stub; 734 if (flags & MIictor) *p++ = &stub; 735 *cast(size_t*)p++ = 0; // number of imported modules 736 assert(cast(void*)p <= &mi + 1); 737 return mi; 738 } 739 740 static void checkExp2(string testname, bool shouldThrow, string oncycle, 741 immutable(ModuleInfo*)[] modules, 742 immutable(ModuleInfo*)[] dtors=null, 743 immutable(ModuleInfo*)[] tlsdtors=null) 744 { 745 auto mgroup = ModuleGroup(modules); 746 mgroup.sortCtors(oncycle); 747 748 // if we are expecting sort to throw, don't throw because of unexpected 749 // success! 750 if (!shouldThrow) 751 { 752 foreach (m; mgroup._modules) 753 assert(!(m.flags & (MIctorstart | MIctordone)), testname); 754 assert(mgroup._ctors == dtors, testname); 755 assert(mgroup._tlsctors == tlsdtors, testname); 756 } 757 } 758 759 static void checkExp(string testname, bool shouldThrow, 760 immutable(ModuleInfo*)[] modules, 761 immutable(ModuleInfo*)[] dtors=null, 762 immutable(ModuleInfo*)[] tlsdtors=null) 763 { 764 checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors); 765 } 766 767 768 { 769 auto m0 = mockMI(0); 770 auto m1 = mockMI(0); 771 auto m2 = mockMI(0); 772 checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 773 } 774 775 { 776 auto m0 = mockMI(MIictor); 777 auto m1 = mockMI(0); 778 auto m2 = mockMI(MIictor); 779 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 780 checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 781 } 782 783 { 784 auto m0 = mockMI(MIstandalone | MIctor); 785 auto m1 = mockMI(0); 786 auto m2 = mockMI(0); 787 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 788 checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]); 789 } 790 791 { 792 auto m0 = mockMI(MIstandalone | MIctor); 793 auto m1 = mockMI(MIstandalone | MIctor); 794 auto m2 = mockMI(0); 795 m1.setImports(&m0.mi); 796 checkExp("imported standalone => no dependency", false, 797 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 798 } 799 800 { 801 auto m0 = mockMI(MIstandalone | MIctor); 802 auto m1 = mockMI(MIstandalone | MIctor); 803 auto m2 = mockMI(0); 804 m0.setImports(&m1.mi); 805 checkExp("imported standalone => no dependency (2)", false, 806 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 807 } 808 809 { 810 auto m0 = mockMI(MIstandalone | MIctor); 811 auto m1 = mockMI(MIstandalone | MIctor); 812 auto m2 = mockMI(0); 813 m0.setImports(&m1.mi); 814 m1.setImports(&m0.mi); 815 checkExp("standalone may have cycle", false, 816 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 817 } 818 819 { 820 auto m0 = mockMI(MIctor); 821 auto m1 = mockMI(MIctor); 822 auto m2 = mockMI(0); 823 m1.setImports(&m0.mi); 824 checkExp("imported ctor => ordered ctors", false, 825 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []); 826 } 827 828 { 829 auto m0 = mockMI(MIctor); 830 auto m1 = mockMI(MIctor); 831 auto m2 = mockMI(0); 832 m0.setImports(&m1.mi); 833 checkExp("imported ctor => ordered ctors (2)", false, 834 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []); 835 } 836 837 { 838 auto m0 = mockMI(MIctor); 839 auto m1 = mockMI(MIctor); 840 auto m2 = mockMI(0); 841 m0.setImports(&m1.mi); 842 m1.setImports(&m0.mi); 843 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 844 "detects ctors cycles"); 845 assertThrown!Throwable(checkExp2("", true, "deprecate", 846 [&m0.mi, &m1.mi, &m2.mi]), 847 "detects ctors cycles (dep)"); 848 } 849 850 { 851 auto m0 = mockMI(MIctor); 852 auto m1 = mockMI(MIctor); 853 auto m2 = mockMI(0); 854 m0.setImports(&m2.mi); 855 m1.setImports(&m2.mi); 856 m2.setImports(&m0.mi, &m1.mi); 857 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 858 "detects cycle with repeats"); 859 } 860 861 { 862 auto m0 = mockMI(MIctor); 863 auto m1 = mockMI(MIctor); 864 auto m2 = mockMI(MItlsctor); 865 m0.setImports(&m1.mi, &m2.mi); 866 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false, 867 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 868 } 869 870 { 871 auto m0 = mockMI(MIctor | MItlsctor); 872 auto m1 = mockMI(MIctor); 873 auto m2 = mockMI(MItlsctor); 874 m0.setImports(&m1.mi, &m2.mi); 875 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false, 876 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]); 877 } 878 879 { 880 auto m0 = mockMI(MIctor); 881 auto m1 = mockMI(MIctor); 882 auto m2 = mockMI(MItlsctor); 883 m0.setImports(&m1.mi, &m2.mi); 884 m2.setImports(&m0.mi); 885 checkExp("no cycle between ctors/tlsctors", false, 886 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 887 } 888 889 { 890 auto m0 = mockMI(MItlsctor); 891 auto m1 = mockMI(MIctor); 892 auto m2 = mockMI(MItlsctor); 893 m0.setImports(&m2.mi); 894 m2.setImports(&m0.mi); 895 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 896 "detects tlsctors cycle"); 897 assertThrown!Throwable(checkExp2("", true, "deprecate", 898 [&m0.mi, &m1.mi, &m2.mi]), 899 "detects tlsctors cycle (dep)"); 900 } 901 902 { 903 auto m0 = mockMI(MItlsctor); 904 auto m1 = mockMI(MIctor); 905 auto m2 = mockMI(MItlsctor); 906 m0.setImports(&m1.mi); 907 m1.setImports(&m0.mi, &m2.mi); 908 m2.setImports(&m1.mi); 909 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 910 "detects tlsctors cycle with repeats"); 911 } 912 913 { 914 auto m0 = mockMI(MIctor); 915 auto m1 = mockMI(MIstandalone | MIctor); 916 auto m2 = mockMI(MIstandalone | MIctor); 917 m0.setImports(&m1.mi); 918 m1.setImports(&m2.mi); 919 m2.setImports(&m0.mi); 920 // NOTE: this is implementation dependent, sorted order shouldn't be tested. 921 checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], 922 [&m1.mi, &m2.mi, &m0.mi]); 923 //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]); 924 } 925 } 926 927 version (CRuntime_Microsoft) 928 { 929 // Dummy so Win32 code can still call it 930 extern(C) void _minit() { } 931 }