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