]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/libdruntime/rt/minfo.d
Add D front-end, libphobos library, and D2 testsuite.
[thirdparty/gcc.git] / libphobos / libdruntime / rt / minfo.d
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
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)
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 (int 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 (int 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 (int i, ref v; initialEdges)
586 v = 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
757 private:
758 immutable(ModuleInfo*)[] _modules;
759 immutable(ModuleInfo)*[] _ctors;
760 immutable(ModuleInfo)*[] _tlsctors;
761 }
762
763
764 /********************************************
765 * Iterate over all module infos.
766 */
767
768 int 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
789 extern (C)
790 {
791 void rt_moduleCtor()
792 {
793 foreach (ref sg; SectionGroup)
794 {
795 sg.moduleGroup.sortCtors();
796 sg.moduleGroup.runCtors();
797 }
798 }
799
800 void rt_moduleTlsCtor()
801 {
802 foreach (ref sg; SectionGroup)
803 {
804 sg.moduleGroup.runTlsCtors();
805 }
806 }
807
808 void rt_moduleTlsDtor()
809 {
810 foreach_reverse (ref sg; SectionGroup)
811 {
812 sg.moduleGroup.runTlsDtors();
813 }
814 }
815
816 void rt_moduleDtor()
817 {
818 foreach_reverse (ref sg; SectionGroup)
819 {
820 sg.moduleGroup.runDtors();
821 sg.moduleGroup.free();
822 }
823 }
824
825 version (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
853 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
854 {
855 foreach (m; modules)
856 {
857 if (auto fp = getfp(m))
858 (*fp)();
859 }
860 }
861
862 void 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
871 unittest
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
1113 version (CRuntime_Microsoft)
1114 {
1115 // Dummy so Win32 code can still call it
1116 extern(C) void _minit() { }
1117 }