145
|
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 (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
|
|
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 }
|