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 src/rt/_minfo.d) 11 */ 12 13module rt.minfo; 14 15import core.stdc.stdlib; // alloca 16import core.stdc.string; // memcpy 17import rt.sections; 18 19enum 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 44struct 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) 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 throw new Error("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) 166 { 167 import core.bitop : bts, btr, bt, BitRange; 168 import rt.util.container.hashtab; 169 170 enum OnCycle 171 { 172 deprecate, 173 abort, 174 print, 175 ignore 176 } 177 178 auto onCycle = OnCycle.abort; 179 180 switch (cycleHandling) with(OnCycle) 181 { 182 case "deprecate": 183 onCycle = deprecate; 184 break; 185 case "abort": 186 onCycle = abort; 187 break; 188 case "print": 189 onCycle = print; 190 break; 191 case "ignore": 192 onCycle = ignore; 193 break; 194 case "": 195 // no option passed 196 break; 197 default: 198 // invalid cycle handling option. 199 throw new Error("DRT invalid cycle handling option: " ~ cycleHandling); 200 } 201 202 debug (printModuleDependencies) 203 { 204 import core.stdc.stdio : printf; 205 206 foreach (_m; _modules) 207 { 208 printf("%s%s%s:", _m.name.ptr, (_m.flags & MIstandalone) 209 ? "+".ptr : "".ptr, (_m.flags & (MIctor | MIdtor)) ? "*".ptr : "".ptr); 210 foreach (_i; _m.importedModules) 211 printf(" %s", _i.name.ptr); 212 printf("\n"); 213 } 214 } 215 216 immutable uint len = cast(uint) _modules.length; 217 if (!len) 218 return; // nothing to do. 219 220 // allocate some stack arrays that will be used throughout the process. 221 immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); 222 immutable flagbytes = nwords * size_t.sizeof; 223 auto ctorstart = cast(size_t*) malloc(flagbytes); // ctor/dtor seen 224 auto ctordone = cast(size_t*) malloc(flagbytes); // ctor/dtor processed 225 auto relevant = cast(size_t*) malloc(flagbytes); // has ctors/dtors 226 scope (exit) 227 { 228 .free(ctorstart); 229 .free(ctordone); 230 .free(relevant); 231 } 232 233 void clearFlags(size_t* flags) 234 { 235 memset(flags, 0, flagbytes); 236 } 237 238 239 // build the edges between each module. We may need this for printing, 240 // and also allows avoiding keeping a hash around for module lookups. 241 int[][] edges = (cast(int[]*)malloc((int[]).sizeof * _modules.length))[0 .. _modules.length]; 242 { 243 HashTab!(immutable(ModuleInfo)*, int) modIndexes; 244 foreach (i, m; _modules) 245 modIndexes[m] = cast(int) i; 246 247 auto reachable = cast(size_t*) malloc(flagbytes); 248 scope(exit) 249 .free(reachable); 250 251 foreach (i, m; _modules) 252 { 253 // use bit array to prevent duplicates 254 // https://issues.dlang.org/show_bug.cgi?id=16208 255 clearFlags(reachable); 256 // preallocate enough space to store all the indexes 257 int *edge = cast(int*)malloc(int.sizeof * _modules.length); 258 size_t nEdges = 0; 259 foreach (imp; m.importedModules) 260 { 261 if (imp is m) // self-import 262 continue; 263 if (auto impidx = imp in modIndexes) 264 { 265 if (!bts(reachable, *impidx)) 266 edge[nEdges++] = *impidx; 267 } 268 } 269 // trim space to what is needed. 270 edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges]; 271 } 272 } 273 274 // free all the edges after we are done 275 scope(exit) 276 { 277 foreach (e; edges) 278 if (e.ptr) 279 .free(e.ptr); 280 .free(edges.ptr); 281 } 282 283 void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) sink) 284 { 285 version (Windows) 286 enum EOL = "\r\n"; 287 else 288 enum EOL = "\n"; 289 290 sink("Cyclic dependency between module "); 291 sink(_modules[sourceIdx].name); 292 sink(" and "); 293 sink(_modules[cycleIdx].name); 294 sink(EOL); 295 auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges); 296 scope(exit) .free(cyclePath.ptr); 297 298 sink(_modules[sourceIdx].name); 299 sink("* ->" ~ EOL); 300 foreach (x; cyclePath[0 .. $ - 1]) 301 { 302 sink(_modules[x].name); 303 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL); 304 } 305 sink(_modules[sourceIdx].name); 306 sink("*" ~ EOL); 307 } 308 309 // find all the non-trivial dependencies (that is, dependencies that have a 310 // ctor or dtor) of a given module. Doing this, we can 'skip over' the 311 // trivial modules to get at the non-trivial ones. 312 // 313 // If a cycle is detected, returns the index of the module that completes the cycle. 314 // Returns: true for success, false for a deprecated cycle error 315 bool findDeps(size_t idx, size_t* reachable) 316 { 317 static struct stackFrame 318 { 319 size_t curMod; 320 size_t curDep; 321 } 322 323 // initialize "stack" 324 auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len); 325 scope (exit) 326 .free(stack); 327 auto stacktop = stack + len; 328 auto sp = stack; 329 sp.curMod = cast(int) idx; 330 sp.curDep = 0; 331 332 // initialize reachable by flagging source module 333 clearFlags(reachable); 334 bts(reachable, idx); 335 336 for (;;) 337 { 338 auto m = _modules[sp.curMod]; 339 if (sp.curDep >= edges[sp.curMod].length) 340 { 341 // return 342 if (sp == stack) // finished the algorithm 343 break; 344 --sp; 345 } 346 else 347 { 348 auto midx = edges[sp.curMod][sp.curDep]; 349 if (!bts(reachable, midx)) 350 { 351 if (bt(relevant, midx)) 352 { 353 // need to process this node, don't recurse. 354 if (bt(ctorstart, midx)) 355 { 356 // was already started, this is a cycle. 357 final switch (onCycle) with(OnCycle) 358 { 359 case deprecate: 360 // check with old algorithm 361 if (sortCtorsOld(edges)) 362 { 363 // unwind to print deprecation message. 364 return false; // deprecated cycle error 365 } 366 goto case abort; // fall through 367 case abort: 368 369 string errmsg = ""; 370 buildCycleMessage(idx, midx, (string x) {errmsg ~= x;}); 371 throw new Error(errmsg, __FILE__, __LINE__); 372 case ignore: 373 break; 374 case print: 375 // print the message 376 buildCycleMessage(idx, midx, (string x) { 377 import core.stdc.stdio : fprintf, stderr; 378 fprintf(stderr, "%.*s", cast(int) x.length, x.ptr); 379 }); 380 // continue on as if this is correct. 381 break; 382 } 383 } 384 } 385 else if (!bt(ctordone, midx)) 386 { 387 // non-relevant, and hasn't been exhaustively processed, recurse. 388 if (++sp >= stacktop) 389 { 390 // stack overflow, this shouldn't happen. 391 import core.internal.abort : abort; 392 393 abort("stack overflow on dependency search"); 394 } 395 sp.curMod = midx; 396 sp.curDep = 0; 397 continue; 398 } 399 } 400 } 401 402 // next dependency 403 ++sp.curDep; 404 } 405 return true; // success 406 } 407 408 // The list of constructors that will be returned by the sorting. 409 immutable(ModuleInfo)** ctors; 410 // current element being inserted into ctors list. 411 size_t ctoridx = 0; 412 413 // This function will determine the order of construction/destruction and 414 // check for cycles. If a cycle is found, the cycle path is transformed 415 // into a string and thrown as an error. 416 // 417 // Each call into this function is given a module that has static 418 // ctor/dtors that must be dealt with. It recurses only when it finds 419 // dependencies that also have static ctor/dtors. 420 // Returns: true for success, false for a deprecated cycle error 421 bool processMod(size_t curidx) 422 { 423 immutable ModuleInfo* current = _modules[curidx]; 424 425 // First, determine what modules are reachable. 426 auto reachable = cast(size_t*) malloc(flagbytes); 427 scope (exit) 428 .free(reachable); 429 if (!findDeps(curidx, reachable)) 430 return false; // deprecated cycle error 431 432 // process the dependencies. First, we process all relevant ones 433 bts(ctorstart, curidx); 434 auto brange = BitRange(reachable, len); 435 foreach (i; brange) 436 { 437 // note, don't check for cycles here, because the config could have been set to ignore cycles. 438 // however, don't recurse if there is one, so still check for started ctor. 439 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i)) 440 { 441 if (!processMod(i)) 442 return false; // deprecated cycle error 443 } 444 } 445 446 // now mark this node, and all nodes reachable from this module as done. 447 bts(ctordone, curidx); 448 btr(ctorstart, curidx); 449 foreach (i; brange) 450 { 451 // Since relevant dependencies are already marked as done 452 // from recursion above (or are going to be handled up the call 453 // stack), no reason to check for relevance, that is a wasted 454 // op. 455 bts(ctordone, i); 456 } 457 458 // add this module to the construction order list 459 ctors[ctoridx++] = current; 460 return true; 461 } 462 463 // returns `false` if deprecated cycle error otherwise set `result`. 464 bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) 465 { 466 clearFlags(relevant); 467 clearFlags(ctorstart); 468 clearFlags(ctordone); 469 470 // pre-allocate enough space to hold all modules. 471 ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof)); 472 ctoridx = 0; 473 foreach (idx, m; _modules) 474 { 475 if (m.flags & relevantFlags) 476 { 477 if (m.flags & MIstandalone) 478 { 479 // can run at any time. Just run it first. 480 ctors[ctoridx++] = m; 481 } 482 else 483 { 484 bts(relevant, idx); 485 } 486 } 487 } 488 489 // now run the algorithm in the relevant ones 490 foreach (idx; BitRange(relevant, len)) 491 { 492 if (!bt(ctordone, idx)) 493 { 494 if (!processMod(idx)) 495 return false; 496 } 497 } 498 499 if (ctoridx == 0) 500 { 501 // no ctors in the list. 502 .free(ctors); 503 } 504 else 505 { 506 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof); 507 if (ctors is null) 508 assert(0); 509 result = ctors[0 .. ctoridx]; 510 } 511 return true; 512 } 513 514 // finally, do the sorting for both shared and tls ctors. If either returns false, 515 // print the deprecation warning. 516 if (!doSort(MIctor | MIdtor, _ctors) || 517 !doSort(MItlsctor | MItlsdtor, _tlsctors)) 518 { 519 // print a warning 520 import core.stdc.stdio : fprintf, stderr; 521 fprintf(stderr, "Deprecation 16211 warning:\n" 522 ~ "A cycle has been detected in your program that was undetected prior to DMD\n" 523 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n" 524 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n"); 525 526 } 527 } 528 529 /// ditto 530 void sortCtors() 531 { 532 import rt.config : rt_configOption; 533 sortCtors(rt_configOption("oncycle")); 534 } 535 536 /****************************** 537 * This is the old ctor sorting algorithm that does not find all cycles. 538 * 539 * It is here to allow the deprecated behavior from the original algorithm 540 * until people have fixed their code. 541 * 542 * If no cycles are found, the _ctors and _tlsctors are replaced with the 543 * ones generated by this algorithm to preserve the old incorrect ordering 544 * behavior. 545 * 546 * Params: 547 * edges - The module edges as found in the `importedModules` member of 548 * each ModuleInfo. Generated in sortCtors. 549 * Returns: 550 * true if no cycle is found, false if one was. 551 */ 552 bool sortCtorsOld(int[][] edges) 553 { 554 immutable len = edges.length; 555 assert(len == _modules.length); 556 557 static struct StackRec 558 { 559 @property int mod() 560 { 561 return _mods[_idx]; 562 } 563 564 int[] _mods; 565 size_t _idx; 566 } 567 568 auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len]; 569 // TODO: reuse GCBits by moving it to rt.util.container or core.internal 570 immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); 571 auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof); 572 auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof); 573 int[] initialEdges = (cast(int *)malloc(int.sizeof * len))[0 .. len]; 574 if (!stack.ptr || ctorstart is null || ctordone is null || !initialEdges.ptr) 575 assert(0); 576 scope (exit) 577 { 578 .free(stack.ptr); 579 .free(ctorstart); 580 .free(ctordone); 581 .free(initialEdges.ptr); 582 } 583 584 // initialize the initial edges 585 foreach (i, ref v; initialEdges) 586 v = cast(int)i; 587 588 bool sort(ref immutable(ModuleInfo)*[] ctors, uint mask) 589 { 590 import core.bitop; 591 592 ctors = (cast(immutable(ModuleInfo)**).malloc(len * size_t.sizeof))[0 .. len]; 593 if (!ctors.ptr) 594 assert(0); 595 596 // clean flags 597 memset(ctorstart, 0, nwords * size_t.sizeof); 598 memset(ctordone, 0, nwords * size_t.sizeof); 599 size_t stackidx = 0; 600 size_t cidx; 601 602 int[] mods = initialEdges; 603 604 size_t idx; 605 while (true) 606 { 607 while (idx < mods.length) 608 { 609 auto m = mods[idx]; 610 611 if (bt(ctordone, m)) 612 { 613 // this module has already been processed, skip 614 ++idx; 615 continue; 616 } 617 else if (bt(ctorstart, m)) 618 { 619 /* Trace back to the begin of the cycle. 620 */ 621 bool ctorInCycle; 622 size_t start = stackidx; 623 while (start--) 624 { 625 auto sm = stack[start].mod; 626 if (sm == m) 627 break; 628 assert(sm >= 0); 629 if (bt(ctorstart, sm)) 630 ctorInCycle = true; 631 } 632 assert(stack[start].mod == m); 633 if (ctorInCycle) 634 { 635 return false; 636 } 637 else 638 { 639 /* This is also a cycle, but the import chain does not constrain 640 * the order of initialization, either because the imported 641 * modules have no ctors or the ctors are standalone. 642 */ 643 ++idx; 644 } 645 } 646 else 647 { 648 auto curmod = _modules[m]; 649 if (curmod.flags & mask) 650 { 651 if (curmod.flags & MIstandalone || !edges[m].length) 652 { // trivial ctor => sort in 653 ctors[cidx++] = curmod; 654 bts(ctordone, m); 655 } 656 else 657 { // non-trivial ctor => defer 658 bts(ctorstart, m); 659 } 660 } 661 else // no ctor => mark as visited 662 { 663 bts(ctordone, m); 664 } 665 666 if (edges[m].length) 667 { 668 /* Internal runtime error, recursion exceeds number of modules. 669 */ 670 (stackidx < len) || assert(0); 671 672 // recurse 673 stack[stackidx++] = StackRec(mods, idx); 674 idx = 0; 675 mods = edges[m]; 676 } 677 } 678 } 679 680 if (stackidx) 681 { // pop old value from stack 682 --stackidx; 683 mods = stack[stackidx]._mods; 684 idx = stack[stackidx]._idx; 685 auto m = mods[idx++]; 686 if (bt(ctorstart, m) && !bts(ctordone, m)) 687 ctors[cidx++] = _modules[m]; 688 } 689 else // done 690 break; 691 } 692 // store final number and shrink array 693 ctors = (cast(immutable(ModuleInfo)**).realloc(ctors.ptr, cidx * size_t.sizeof))[0 .. cidx]; 694 return true; 695 } 696 697 /* Do two passes: ctor/dtor, tlsctor/tlsdtor 698 */ 699 immutable(ModuleInfo)*[] _ctors2; 700 immutable(ModuleInfo)*[] _tlsctors2; 701 auto result = sort(_ctors2, MIctor | MIdtor) && sort(_tlsctors2, MItlsctor | MItlsdtor); 702 if (result) // no cycle 703 { 704 // fall back to original ordering as part of the deprecation. 705 if (_ctors.ptr) 706 .free(_ctors.ptr); 707 _ctors = _ctors2; 708 if (_tlsctors.ptr) 709 .free(_tlsctors.ptr); 710 _tlsctors = _tlsctors2; 711 } 712 else 713 { 714 // free any allocated memory that will be forgotten 715 if (_ctors2.ptr) 716 .free(_ctors2.ptr); 717 if (_tlsctors2.ptr) 718 .free(_tlsctors2.ptr); 719 } 720 return result; 721 } 722 723 void runCtors() 724 { 725 // run independent ctors 726 runModuleFuncs!(m => m.ictor)(_modules); 727 // sorted module ctors 728 runModuleFuncs!(m => m.ctor)(_ctors); 729 } 730 731 void runTlsCtors() 732 { 733 runModuleFuncs!(m => m.tlsctor)(_tlsctors); 734 } 735 736 void runTlsDtors() 737 { 738 runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors); 739 } 740 741 void runDtors() 742 { 743 runModuleFuncsRev!(m => m.dtor)(_ctors); 744 } 745 746 void free() 747 { 748 if (_ctors.ptr) 749 .free(_ctors.ptr); 750 _ctors = null; 751 if (_tlsctors.ptr) 752 .free(_tlsctors.ptr); 753 _tlsctors = null; 754 // _modules = null; // let the owner free it 755 } 756 757private: 758 immutable(ModuleInfo*)[] _modules; 759 immutable(ModuleInfo)*[] _ctors; 760 immutable(ModuleInfo)*[] _tlsctors; 761} 762 763 764/******************************************** 765 * Iterate over all module infos. 766 */ 767 768int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg) 769{ 770 foreach (ref sg; SectionGroup) 771 { 772 foreach (m; sg.modules) 773 { 774 // TODO: Should null ModuleInfo be allowed? 775 if (m !is null) 776 { 777 if (auto res = dg(m)) 778 return res; 779 } 780 } 781 } 782 return 0; 783} 784 785/******************************************** 786 * Module constructor and destructor routines. 787 */ 788 789extern (C) 790{ 791void rt_moduleCtor() 792{ 793 foreach (ref sg; SectionGroup) 794 { 795 sg.moduleGroup.sortCtors(); 796 sg.moduleGroup.runCtors(); 797 } 798} 799 800void rt_moduleTlsCtor() 801{ 802 foreach (ref sg; SectionGroup) 803 { 804 sg.moduleGroup.runTlsCtors(); 805 } 806} 807 808void rt_moduleTlsDtor() 809{ 810 foreach_reverse (ref sg; SectionGroup) 811 { 812 sg.moduleGroup.runTlsDtors(); 813 } 814} 815 816void rt_moduleDtor() 817{ 818 foreach_reverse (ref sg; SectionGroup) 819 { 820 sg.moduleGroup.runDtors(); 821 sg.moduleGroup.free(); 822 } 823} 824 825version (Win32) 826{ 827 // Alternate names for backwards compatibility with older DLL code 828 void _moduleCtor() 829 { 830 rt_moduleCtor(); 831 } 832 833 void _moduleDtor() 834 { 835 rt_moduleDtor(); 836 } 837 838 void _moduleTlsCtor() 839 { 840 rt_moduleTlsCtor(); 841 } 842 843 void _moduleTlsDtor() 844 { 845 rt_moduleTlsDtor(); 846 } 847} 848} 849 850/******************************************** 851 */ 852 853void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 854{ 855 foreach (m; modules) 856 { 857 if (auto fp = getfp(m)) 858 (*fp)(); 859 } 860} 861 862void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules) 863{ 864 foreach_reverse (m; modules) 865 { 866 if (auto fp = getfp(m)) 867 (*fp)(); 868 } 869} 870 871unittest 872{ 873 static void assertThrown(T : Throwable, E)(lazy E expr, string msg) 874 { 875 try 876 expr; 877 catch (T) 878 return; 879 assert(0, msg); 880 } 881 882 static void stub() 883 { 884 } 885 886 static struct UTModuleInfo 887 { 888 this(uint flags) 889 { 890 mi._flags = flags; 891 } 892 893 void setImports(immutable(ModuleInfo)*[] imports...) 894 { 895 import core.bitop; 896 assert(flags & MIimportedModules); 897 898 immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor)); 899 immutable size = nfuncs * (void function()).sizeof + 900 size_t.sizeof + imports.length * (ModuleInfo*).sizeof; 901 assert(size <= pad.sizeof); 902 903 pad[nfuncs] = imports.length; 904 .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof); 905 } 906 907 immutable ModuleInfo mi; 908 size_t[8] pad; 909 alias mi this; 910 } 911 912 static UTModuleInfo mockMI(uint flags) 913 { 914 auto mi = UTModuleInfo(flags | MIimportedModules); 915 auto p = cast(void function()*)&mi.pad; 916 if (flags & MItlsctor) *p++ = &stub; 917 if (flags & MItlsdtor) *p++ = &stub; 918 if (flags & MIctor) *p++ = &stub; 919 if (flags & MIdtor) *p++ = &stub; 920 if (flags & MIictor) *p++ = &stub; 921 *cast(size_t*)p++ = 0; // number of imported modules 922 assert(cast(void*)p <= &mi + 1); 923 return mi; 924 } 925 926 static void checkExp2(string testname, bool shouldThrow, string oncycle, 927 immutable(ModuleInfo*)[] modules, 928 immutable(ModuleInfo*)[] dtors=null, 929 immutable(ModuleInfo*)[] tlsdtors=null) 930 { 931 auto mgroup = ModuleGroup(modules); 932 mgroup.sortCtors(oncycle); 933 934 // if we are expecting sort to throw, don't throw because of unexpected 935 // success! 936 if (!shouldThrow) 937 { 938 foreach (m; mgroup._modules) 939 assert(!(m.flags & (MIctorstart | MIctordone)), testname); 940 assert(mgroup._ctors == dtors, testname); 941 assert(mgroup._tlsctors == tlsdtors, testname); 942 } 943 } 944 945 static void checkExp(string testname, bool shouldThrow, 946 immutable(ModuleInfo*)[] modules, 947 immutable(ModuleInfo*)[] dtors=null, 948 immutable(ModuleInfo*)[] tlsdtors=null) 949 { 950 checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors); 951 } 952 953 954 { 955 auto m0 = mockMI(0); 956 auto m1 = mockMI(0); 957 auto m2 = mockMI(0); 958 checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 959 } 960 961 { 962 auto m0 = mockMI(MIictor); 963 auto m1 = mockMI(0); 964 auto m2 = mockMI(MIictor); 965 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 966 checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]); 967 } 968 969 { 970 auto m0 = mockMI(MIstandalone | MIctor); 971 auto m1 = mockMI(0); 972 auto m2 = mockMI(0); 973 auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); 974 checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]); 975 } 976 977 { 978 auto m0 = mockMI(MIstandalone | MIctor); 979 auto m1 = mockMI(MIstandalone | MIctor); 980 auto m2 = mockMI(0); 981 m1.setImports(&m0.mi); 982 checkExp("imported standalone => no dependency", false, 983 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 984 } 985 986 { 987 auto m0 = mockMI(MIstandalone | MIctor); 988 auto m1 = mockMI(MIstandalone | MIctor); 989 auto m2 = mockMI(0); 990 m0.setImports(&m1.mi); 991 checkExp("imported standalone => no dependency (2)", false, 992 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 993 } 994 995 { 996 auto m0 = mockMI(MIstandalone | MIctor); 997 auto m1 = mockMI(MIstandalone | MIctor); 998 auto m2 = mockMI(0); 999 m0.setImports(&m1.mi); 1000 m1.setImports(&m0.mi); 1001 checkExp("standalone may have cycle", false, 1002 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); 1003 } 1004 1005 { 1006 auto m0 = mockMI(MIctor); 1007 auto m1 = mockMI(MIctor); 1008 auto m2 = mockMI(0); 1009 m1.setImports(&m0.mi); 1010 checkExp("imported ctor => ordered ctors", false, 1011 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []); 1012 } 1013 1014 { 1015 auto m0 = mockMI(MIctor); 1016 auto m1 = mockMI(MIctor); 1017 auto m2 = mockMI(0); 1018 m0.setImports(&m1.mi); 1019 checkExp("imported ctor => ordered ctors (2)", false, 1020 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []); 1021 } 1022 1023 { 1024 auto m0 = mockMI(MIctor); 1025 auto m1 = mockMI(MIctor); 1026 auto m2 = mockMI(0); 1027 m0.setImports(&m1.mi); 1028 m1.setImports(&m0.mi); 1029 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 1030 "detects ctors cycles"); 1031 assertThrown!Throwable(checkExp2("", true, "deprecate", 1032 [&m0.mi, &m1.mi, &m2.mi]), 1033 "detects ctors cycles (dep)"); 1034 } 1035 1036 { 1037 auto m0 = mockMI(MIctor); 1038 auto m1 = mockMI(MIctor); 1039 auto m2 = mockMI(0); 1040 m0.setImports(&m2.mi); 1041 m1.setImports(&m2.mi); 1042 m2.setImports(&m0.mi, &m1.mi); 1043 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 1044 "detects cycle with repeats"); 1045 } 1046 1047 { 1048 auto m0 = mockMI(MIctor); 1049 auto m1 = mockMI(MIctor); 1050 auto m2 = mockMI(MItlsctor); 1051 m0.setImports(&m1.mi, &m2.mi); 1052 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false, 1053 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 1054 } 1055 1056 { 1057 auto m0 = mockMI(MIctor | MItlsctor); 1058 auto m1 = mockMI(MIctor); 1059 auto m2 = mockMI(MItlsctor); 1060 m0.setImports(&m1.mi, &m2.mi); 1061 checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false, 1062 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]); 1063 } 1064 1065 { 1066 auto m0 = mockMI(MIctor); 1067 auto m1 = mockMI(MIctor); 1068 auto m2 = mockMI(MItlsctor); 1069 m0.setImports(&m1.mi, &m2.mi); 1070 m2.setImports(&m0.mi); 1071 checkExp("no cycle between ctors/tlsctors", false, 1072 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); 1073 } 1074 1075 { 1076 auto m0 = mockMI(MItlsctor); 1077 auto m1 = mockMI(MIctor); 1078 auto m2 = mockMI(MItlsctor); 1079 m0.setImports(&m2.mi); 1080 m2.setImports(&m0.mi); 1081 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 1082 "detects tlsctors cycle"); 1083 assertThrown!Throwable(checkExp2("", true, "deprecate", 1084 [&m0.mi, &m1.mi, &m2.mi]), 1085 "detects tlsctors cycle (dep)"); 1086 } 1087 1088 { 1089 auto m0 = mockMI(MItlsctor); 1090 auto m1 = mockMI(MIctor); 1091 auto m2 = mockMI(MItlsctor); 1092 m0.setImports(&m1.mi); 1093 m1.setImports(&m0.mi, &m2.mi); 1094 m2.setImports(&m1.mi); 1095 assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]), 1096 "detects tlsctors cycle with repeats"); 1097 } 1098 1099 { 1100 auto m0 = mockMI(MIctor); 1101 auto m1 = mockMI(MIstandalone | MIctor); 1102 auto m2 = mockMI(MIstandalone | MIctor); 1103 m0.setImports(&m1.mi); 1104 m1.setImports(&m2.mi); 1105 m2.setImports(&m0.mi); 1106 // NOTE: this is implementation dependent, sorted order shouldn't be tested. 1107 checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], 1108 [&m1.mi, &m2.mi, &m0.mi]); 1109 //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]); 1110 } 1111} 1112 1113version (CRuntime_Microsoft) 1114{ 1115 // Dummy so Win32 code can still call it 1116 extern(C) void _minit() { } 1117} 1118