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 // trim space to what is needed. 271 edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges]; 272 } 273 } 274 275 // free all the edges after we are done 276 scope(exit) 277 { 278 foreach (e; edges) 279 if (e.ptr) 280 .free(e.ptr); 281 .free(edges.ptr); 282 } 283 284 void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink) 285 { 286 version (Windows) 287 enum EOL = "\r\n"; 288 else 289 enum EOL = "\n"; 290 291 sink("Cyclic dependency between module constructors/destructors of "); 292 sink(_modules[sourceIdx].name); 293 sink(" and "); 294 sink(_modules[cycleIdx].name); 295 sink(EOL); 296 auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges); 297 scope(exit) .free(cyclePath.ptr); 298 299 sink(_modules[sourceIdx].name); 300 sink("* ->" ~ EOL); 301 foreach (x; cyclePath[0 .. $ - 1]) 302 { 303 sink(_modules[x].name); 304 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL); 305 } 306 sink(_modules[sourceIdx].name); 307 sink("*" ~ EOL); 308 } 309 310 // find all the non-trivial dependencies (that is, dependencies that have a 311 // ctor or dtor) of a given module. Doing this, we can 'skip over' the 312 // trivial modules to get at the non-trivial ones. 313 // 314 // If a cycle is detected, returns the index of the module that completes the cycle. 315 // Returns: true for success, false for a deprecated cycle error 316 bool findDeps(size_t idx, size_t* reachable) nothrow 317 { 318 static struct stackFrame 319 { 320 size_t curMod; 321 size_t curDep; 322 } 323 324 // initialize "stack" 325 auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len); 326 scope (exit) 327 .free(stack); 328 auto stacktop = stack + len; 329 auto sp = stack; 330 sp.curMod = cast(int) idx; 331 sp.curDep = 0; 332 333 // initialize reachable by flagging source module 334 clearFlags(reachable); 335 bts(reachable, idx); 336 337 for (;;) 338 { 339 auto m = _modules[sp.curMod]; 340 if (sp.curDep >= edges[sp.curMod].length) 341 { 342 // return 343 if (sp == stack) // finished the algorithm 344 break; 345 --sp; 346 } 347 else 348 { 349 auto midx = edges[sp.curMod][sp.curDep]; 350 if (!bts(reachable, midx)) 351 { 352 if (bt(relevant, midx)) 353 { 354 // need to process this node, don't recurse. 355 if (bt(ctorstart, midx)) 356 { 357 // was already started, this is a cycle. 358 final switch (onCycle) with(OnCycle) 359 { 360 case abort: 361 362 string errmsg = ""; 363 buildCycleMessage(idx, midx, (string x) {errmsg ~= x;}); 364 throw new Error(errmsg, __FILE__, __LINE__); 365 case ignore: 366 break; 367 case print: 368 // print the message 369 buildCycleMessage(idx, midx, (string x) { 370 import core.stdc.stdio : fprintf, stderr; 371 fprintf(stderr, "%.*s", cast(int) x.length, x.ptr); 372 }); 373 // continue on as if this is correct. 374 break; 375 } 376 } 377 } 378 else if (!bt(ctordone, midx)) 379 { 380 // non-relevant, and hasn't been exhaustively processed, recurse. 381 if (++sp >= stacktop) 382 { 383 // stack overflow, this shouldn't happen. 384 import core.internal.abort : abort; 385 386 abort("stack overflow on dependency search"); 387 } 388 sp.curMod = midx; 389 sp.curDep = 0; 390 continue; 391 } 392 } 393 } 394 395 // next dependency 396 ++sp.curDep; 397 } 398 return true; // success 399 } 400 401 // The list of constructors that will be returned by the sorting. 402 immutable(ModuleInfo)** ctors; 403 // current element being inserted into ctors list. 404 size_t ctoridx = 0; 405 406 // This function will determine the order of construction/destruction and 407 // check for cycles. If a cycle is found, the cycle path is transformed 408 // into a string and thrown as an error. 409 // 410 // Each call into this function is given a module that has static 411 // ctor/dtors that must be dealt with. It recurses only when it finds 412 // dependencies that also have static ctor/dtors. 413 // Returns: true for success, false for a deprecated cycle error 414 bool processMod(size_t curidx) nothrow 415 { 416 immutable ModuleInfo* current = _modules[curidx]; 417 418 // First, determine what modules are reachable. 419 auto reachable = cast(size_t*) malloc(flagbytes); 420 scope (exit) 421 .free(reachable); 422 if (!findDeps(curidx, reachable)) 423 return false; // deprecated cycle error 424 425 // process the dependencies. First, we process all relevant ones 426 bts(ctorstart, curidx); 427 auto brange = BitRange(reachable, len); 428 foreach (i; brange) 429 { 430 // note, don't check for cycles here, because the config could have been set to ignore cycles. 431 // however, don't recurse if there is one, so still check for started ctor. 432 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i)) 433 { 434 if (!processMod(i)) 435 return false; // deprecated cycle error 436 } 437 } 438 439 // now mark this node, and all nodes reachable from this module as done. 440 bts(ctordone, curidx); 441 btr(ctorstart, curidx); 442 foreach (i; brange) 443 { 444 // Since relevant dependencies are already marked as done 445 // from recursion above (or are going to be handled up the call 446 // stack), no reason to check for relevance, that is a wasted 447 // op. 448 bts(ctordone, i); 449 } 450 451 // add this module to the construction order list 452 ctors[ctoridx++] = current; 453 return true; 454 } 455 456 // returns `false` if deprecated cycle error otherwise set `result`. 457 bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow 458 { 459 clearFlags(relevant); 460 clearFlags(ctorstart); 461 clearFlags(ctordone); 462 463 // pre-allocate enough space to hold all modules. 464 ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof)); 465 ctoridx = 0; 466 foreach (idx, m; _modules) 467 { 468 if (m.flags & relevantFlags) 469 { 470 if (m.flags & MIstandalone) 471 { 472 // can run at any time. Just run it first. 473 ctors[ctoridx++] = m; 474 } 475 else 476 { 477 bts(relevant, idx); 478 } 479 } 480 } 481 482 // now run the algorithm in the relevant ones 483 foreach (idx; BitRange(relevant, len)) 484 { 485 if (!bt(ctordone, idx)) 486 { 487 if (!processMod(idx)) 488 return false; 489 } 490 } 491 492 if (ctoridx == 0) 493 { 494 // no ctors in the list. 495 .free(ctors); 496 } 497 else 498 { 499 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof); 500 if (ctors is null) 501 assert(0); 502 result = ctors[0 .. ctoridx]; 503 } 504 return true; 505 } 506 507 // finally, do the sorting for both shared and tls ctors. If either returns false, 508 // print the deprecation warning. 509 if (!doSort(MIctor | MIdtor, _ctors) || 510 !doSort(MItlsctor | MItlsdtor, _tlsctors)) 511 { 512 // print a warning 513 import core.stdc.stdio : fprintf, stderr; 514 fprintf(stderr, "Deprecation 16211 warning:\n" 515 ~ "A cycle has been detected in your program that was undetected prior to DMD\n" 516 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n" 517 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n"); 518 519 } 520 } 521 522 /// ditto 523 void sortCtors() 524 { 525 import rt.config : rt_configOption; 526 sortCtors(rt_configOption("oncycle")); 527 } 528 529 void runCtors() 530 { 531 // run independent ctors 532 runModuleFuncs!(m => m.ictor)(_modules); 533 // sorted module ctors 534 runModuleFuncs!(m => m.ctor)(_ctors); 535 } 536 537 void runTlsCtors() 538 { 539 runModuleFuncs!(m => m.tlsctor)(_tlsctors); 540 } 541 542 void runTlsDtors() 543 { 544 runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors); 545 } 546 547 void runDtors() 548 { 549 runModuleFuncsRev!(m => m.dtor)(_ctors); 550 } 551 552 void free() 553 { 554 if (_ctors.ptr) 555 .free(_ctors.ptr); 556 _ctors = null; 557 if (_tlsctors.ptr) 558 .free(_tlsctors.ptr); 559 _tlsctors = null; 560 // _modules = null; // let the owner free it 561 } 562 563 private: 564 immutable(ModuleInfo*)[] _modules; 565 immutable(ModuleInfo)*[] _ctors; 566 immutable(ModuleInfo)*[] _tlsctors; 567 } 568 569 570 /******************************************** 571 * Iterate over all module infos. 572 */ 573 574 int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg) 575 { 576 foreach (ref sg; SectionGroup) 577 { 578 foreach (m; sg.modules) 579 { 580 // TODO: Should null ModuleInfo be allowed? 581 if (m !is null) 582 { 583 if (auto res = dg(m)) 584 return res; 585 } 586 } 587 } 588 return 0; 589 } 590 591 /******************************************** 592 * Module constructor and destructor routines. 593 */ 594 595 extern (C) 596 { 597 void rt_moduleCtor() 598 { 599 foreach (ref sg; SectionGroup) 600 { 601 sg.moduleGroup.sortCtors(); 602 sg.moduleGroup.runCtors(); 603 } 604 } 605 606 void rt_moduleTlsCtor() 607 { 608 foreach (ref sg; SectionGroup) 609 { 610 sg.moduleGroup.runTlsCtors(); 611 } 612 } 613 614 void rt_moduleTlsDtor() 615 { 616 foreach_reverse (ref sg; SectionGroup) 617 { 618 sg.moduleGroup.runTlsDtors(); 619 } 620 } 621 622 void rt_moduleDtor() 623 { 624 foreach_reverse (ref sg; SectionGroup) 625 { 626 sg.moduleGroup.runDtors(); 627 sg.moduleGroup.free(); 628 } 629 } 630 631 version (Win32) 632 { 633 // Alternate names for backwards compatibility with older DLL code 634 void _moduleCtor() 635 { 636 rt_moduleCtor(); 637 } 638 639 void _moduleDtor() 640 { 641 rt_moduleDtor(); 642 } 643 644 void _moduleTlsCtor() 645 { 646 rt_moduleTlsCtor(); 647 } 648 649 void _moduleTlsDtor() 650 { 651 rt_moduleTlsDtor(); 652 } 653 } 654 } 655 656 /******************************************** 657 */ 658 659 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 660 { 661 foreach (m; modules) 662 { 663 if (auto fp = getfp(m)) 664 (*fp)(); 665 } 666 } 667 668 void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 669 { 670 foreach_reverse (m; modules) 671 { 672 if (auto fp = getfp(m)) 673 (*fp)(); 674 } 675 } 676 677 unittest 678 { 679 static void assertThrown(T : Throwable, E)(lazy E expr, string msg) 680 { 681 try 682 expr; 683 catch (T) 684 return; 685 assert(0, msg); 686 } 687 688 static void stub() 689 { 690 } 691 692 static struct UTModuleInfo 693 { 694 this(uint flags) 695 { 696 mi._flags = flags; 697 } 698 699 void setImports(immutable(ModuleInfo)*[] imports...) 700 { 701 import core.bitop; 702 assert(flags & MIimportedModules); 703 704 immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor)); 705 immutable size = nfuncs * (void function()).sizeof + 706 size_t.sizeof + imports.length * (ModuleInfo*).sizeof; 707 assert(size <= pad.sizeof); 708 709 pad[nfuncs] = imports.length; 710 .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof); 711 } 712 713 immutable ModuleInfo mi; 714 size_t[8] pad; 715 alias mi this; 716 } 717 718 static UTModuleInfo mockMI(uint flags) 719 { 720 auto mi = UTModuleInfo(flags | MIimportedModules); 721 auto p = cast(void function()*)&mi.pad; 722 if (flags & MItlsctor) *p++ = &stub; 723 if (flags & MItlsdtor) *p++ = &stub; 724 if (flags & MIctor) *p++ = &stub; 725 if (flags & MIdtor) *p++ = &stub; 726 if (flags & MIictor) *p++ = &stub; 727 *cast(size_t*)p++ = 0; // number of imported modules 728 assert(cast(void*)p <= &mi + 1); 729 return mi; 730 } 731 732 static void checkExp2(string testname, bool shouldThrow, string oncycle, 733 immutable(ModuleInfo*)[] modules, 734 immutable(ModuleInfo*)[] dtors=null, 735 immutable(ModuleInfo*)[] tlsdtors=null) 736 { 737 auto mgroup = ModuleGroup(modules); 738 mgroup.sortCtors(oncycle); 739 740 // if we are expecting sort to throw, don't throw because of unexpected 741 // success! 742 if (!shouldThrow) 743 { 744 foreach (m; mgroup._modules) 745 assert(!(m.flags & (MIctorstart | MIctordone)), testname); 746 assert(mgroup._ctors == dtors, testname); 747 assert(mgroup._tlsctors == tlsdtors, testname); 748 } 749 } 750 751 static void checkExp(string testname, bool shouldThrow, 752 immutable(ModuleInfo*)[] modules, 753 immutable(ModuleInfo*)[] dtors=null, 754 immutable(ModuleInfo*)[] tlsdtors=null) 755 { 756 checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors); 757 } 758 759 760 { 761 auto m0 = mockMI(0); 762 auto m1 = mockMI(0); 763 auto m2 = mockMI(0); 764 checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 765 } 766 767 { 768 auto m0 = mockMI(MIictor); 769 auto m1 = mockMI(0); 770 auto m2 = mockMI(MIictor); 771 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 772 checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 773 } 774 775 { 776 auto m0 = mockMI(MIstandalone | MIctor); 777 auto m1 = mockMI(0); 778 auto m2 = mockMI(0); 779 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 780 checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]); 781 } 782 783 { 784 auto m0 = mockMI(MIstandalone | MIctor); 785 auto m1 = mockMI(MIstandalone | MIctor); 786 auto m2 = mockMI(0); 787 m1.setImports(&m0.mi); 788 checkExp("imported standalone => no dependency", false, 789 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 790 } 791 792 { 793 auto m0 = mockMI(MIstandalone | MIctor); 794 auto m1 = mockMI(MIstandalone | MIctor); 795 auto m2 = mockMI(0); 796 m0.setImports(&m1.mi); 797 checkExp("imported standalone => no dependency (2)", false, 798 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 799 } 800 801 { 802 auto m0 = mockMI(MIstandalone | MIctor); 803 auto m1 = mockMI(MIstandalone | MIctor); 804 auto m2 = mockMI(0); 805 m0.setImports(&m1.mi); 806 m1.setImports(&m0.mi); 807 checkExp("standalone may have cycle", false, 808 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 809 } 810 811 { 812 auto m0 = mockMI(MIctor); 813 auto m1 = mockMI(MIctor); 814 auto m2 = mockMI(0); 815 m1.setImports(&m0.mi); 816 checkExp("imported ctor => ordered ctors", false, 817 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []); 818 } 819 820 { 821 auto m0 = mockMI(MIctor); 822 auto m1 = mockMI(MIctor); 823 auto m2 = mockMI(0); 824 m0.setImports(&m1.mi); 825 checkExp("imported ctor => ordered ctors (2)", false, 826 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []); 827 } 828 829 { 830 auto m0 = mockMI(MIctor); 831 auto m1 = mockMI(MIctor); 832 auto m2 = mockMI(0); 833 m0.setImports(&m1.mi); 834 m1.setImports(&m0.mi); 835 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 836 "detects ctors cycles"); 837 assertThrown!Throwable(checkExp2("", true, "deprecate", 838 [&m0.mi, &m1.mi, &m2.mi]), 839 "detects ctors cycles (dep)"); 840 } 841 842 { 843 auto m0 = mockMI(MIctor); 844 auto m1 = mockMI(MIctor); 845 auto m2 = mockMI(0); 846 m0.setImports(&m2.mi); 847 m1.setImports(&m2.mi); 848 m2.setImports(&m0.mi, &m1.mi); 849 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 850 "detects cycle with repeats"); 851 } 852 853 { 854 auto m0 = mockMI(MIctor); 855 auto m1 = mockMI(MIctor); 856 auto m2 = mockMI(MItlsctor); 857 m0.setImports(&m1.mi, &m2.mi); 858 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false, 859 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 860 } 861 862 { 863 auto m0 = mockMI(MIctor | MItlsctor); 864 auto m1 = mockMI(MIctor); 865 auto m2 = mockMI(MItlsctor); 866 m0.setImports(&m1.mi, &m2.mi); 867 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false, 868 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]); 869 } 870 871 { 872 auto m0 = mockMI(MIctor); 873 auto m1 = mockMI(MIctor); 874 auto m2 = mockMI(MItlsctor); 875 m0.setImports(&m1.mi, &m2.mi); 876 m2.setImports(&m0.mi); 877 checkExp("no cycle between ctors/tlsctors", false, 878 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 879 } 880 881 { 882 auto m0 = mockMI(MItlsctor); 883 auto m1 = mockMI(MIctor); 884 auto m2 = mockMI(MItlsctor); 885 m0.setImports(&m2.mi); 886 m2.setImports(&m0.mi); 887 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 888 "detects tlsctors cycle"); 889 assertThrown!Throwable(checkExp2("", true, "deprecate", 890 [&m0.mi, &m1.mi, &m2.mi]), 891 "detects tlsctors cycle (dep)"); 892 } 893 894 { 895 auto m0 = mockMI(MItlsctor); 896 auto m1 = mockMI(MIctor); 897 auto m2 = mockMI(MItlsctor); 898 m0.setImports(&m1.mi); 899 m1.setImports(&m0.mi, &m2.mi); 900 m2.setImports(&m1.mi); 901 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 902 "detects tlsctors cycle with repeats"); 903 } 904 905 { 906 auto m0 = mockMI(MIctor); 907 auto m1 = mockMI(MIstandalone | MIctor); 908 auto m2 = mockMI(MIstandalone | MIctor); 909 m0.setImports(&m1.mi); 910 m1.setImports(&m2.mi); 911 m2.setImports(&m0.mi); 912 // NOTE: this is implementation dependent, sorted order shouldn't be tested. 913 checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], 914 [&m1.mi, &m2.mi, &m0.mi]); 915 //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]); 916 } 917 } 918 919 version (CRuntime_Microsoft) 920 { 921 // Dummy so Win32 code can still call it 922 extern(C) void _minit() { } 923 }