1 module bc.core.system.linux.dwarf;
2
3 version (D_BetterC) {}
4 else version (linux):
5
6 import bc.core.system.linux.elf : Image;
7 import core.exception : onOutOfMemoryErrorNoGC;
8 import core.internal.traits : dtorIsNothrow;
9 import core.stdc.stdio : snprintf;
10 import core.stdc..string : strlen;
11
12 // Selective copy from normally unavailable: https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d
13 // Also combined (and noted) with some only here used module parts
14
15 size_t getFirstFrame(const(void*)[] callstack, const char** frameList) nothrow @nogc
16 {
17 import core.internal.execinfo : getMangledSymbolName;
18
19 version (LDC) enum BaseExceptionFunctionName = "_d_throw_exception";
20 else enum BaseExceptionFunctionName = "_d_throwdwarf";
21
22 foreach (i; 0..callstack.length)
23 {
24 auto proc = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
25 if (proc == BaseExceptionFunctionName) return i+1;
26 }
27 return 0;
28 }
29
30 /// Our customized @nogc variant of https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d#L94
31 size_t dumpCallstack(S)(ref S sink, ref Image image, const(void*)[] callstack, const char** frameList,
32 const(ubyte)[] debugLineSectionData) nothrow @nogc
33 {
34 // find address -> file, line mapping using dwarf debug_line
35 Array!Location locations;
36 if (debugLineSectionData)
37 {
38 locations.length = callstack.length;
39 foreach (size_t i; 0 .. callstack.length)
40 locations[i].address = cast(size_t) callstack[i];
41
42 resolveAddresses(debugLineSectionData, locations[], image.baseAddress);
43 }
44
45 size_t ret = 0;
46 foreach (size_t i; 0 .. callstack.length)
47 {
48 char[1536] buffer = void;
49 size_t bufferLength = 0;
50
51 void appendToBuffer(Args...)(const(char)* format, Args args)
52 {
53 const count = snprintf(buffer.ptr + bufferLength, buffer.length - bufferLength, format, args);
54 assert(count >= 0);
55 bufferLength += count;
56 if (bufferLength >= buffer.length)
57 bufferLength = buffer.length - 1;
58 }
59
60 if (i) { sink.put('\n'); ret++; }
61
62 if (locations.length > 0 && locations[i].line != -1)
63 {
64 bool includeSlash = locations[i].directory.length > 0 && locations[i].directory[$ - 1] != '/';
65 if (locations[i].line)
66 {
67 string printFormat = includeSlash ? "%.*s/%.*s:%d " : "%.*s%.*s:%d ";
68 appendToBuffer(
69 printFormat.ptr,
70 cast(int) locations[i].directory.length, locations[i].directory.ptr,
71 cast(int) locations[i].file.length, locations[i].file.ptr,
72 locations[i].line,
73 );
74 }
75 else
76 {
77 string printFormat = includeSlash ? "%.*s/%.*s " : "%.*s%.*s ";
78 appendToBuffer(
79 printFormat.ptr,
80 cast(int) locations[i].directory.length, locations[i].directory.ptr,
81 cast(int) locations[i].file.length, locations[i].file.ptr,
82 );
83 }
84 }
85 else
86 {
87 buffer[0 .. 5] = "??:? ";
88 bufferLength = 5;
89 }
90
91 char[1024] symbolBuffer = void;
92 auto symbol = getDemangledSymbol(frameList[i][0 .. strlen(frameList[i])], symbolBuffer);
93 if (symbol.length > 0)
94 appendToBuffer("%.*s ", cast(int) symbol.length, symbol.ptr);
95
96 const addressLength = 20;
97 const maxBufferLength = buffer.length - addressLength;
98 if (bufferLength > maxBufferLength)
99 {
100 buffer[maxBufferLength-4 .. maxBufferLength] = "... ";
101 bufferLength = maxBufferLength;
102 }
103 appendToBuffer("[0x%zx]", callstack[i]);
104
105 auto output = buffer[0 .. bufferLength];
106 sink.put(output);
107 ret += bufferLength;
108 if (symbol == "_Dmain") break;
109 }
110
111 return ret;
112 }
113
114 // Copy: https://github.com/dlang/druntime/blob/master/src/rt/backtrace/dwarf.d#L172
115 // the lifetime of the Location data is bound to the lifetime of debugLineSectionData
116 void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow
117 {
118 debug(DwarfDebugMachine) import core.stdc.stdio;
119
120 size_t numberOfLocationsFound = 0;
121
122 const(ubyte)[] dbg = debugLineSectionData;
123 while (dbg.length > 0)
124 {
125 debug(DwarfDebugMachine) printf("new debug program\n");
126 const lp = readLineNumberProgram(dbg);
127
128 LocationInfo lastLoc = LocationInfo(-1, -1);
129 size_t lastAddress = 0x0;
130
131 debug(DwarfDebugMachine) printf("program:\n");
132 runStateMachine(lp,
133 (size_t address, LocationInfo locInfo, bool isEndSequence)
134 {
135 // adjust to ASLR offset
136 address += baseAddress;
137 debug (DwarfDebugMachine)
138 printf("-- offsetting 0x%zx to 0x%zx\n", address - baseAddress, address);
139
140 foreach (ref loc; locations)
141 {
142 // If loc.line != -1, then it has been set previously.
143 // Some implementations (eg. dmd) write an address to
144 // the debug data multiple times, but so far I have found
145 // that the first occurrence to be the correct one.
146 if (loc.line != -1)
147 continue;
148
149 // Can be called with either `locInfo` or `lastLoc`
150 void update(const ref LocationInfo match)
151 {
152 const sourceFile = lp.sourceFiles[match.file - 1];
153 debug (DwarfDebugMachine)
154 {
155 printf("-- found for [0x%zx]:\n", loc.address);
156 printf("-- file: %.*s\n",
157 cast(int) sourceFile.file.length, sourceFile.file.ptr);
158 printf("-- line: %d\n", match.line);
159 }
160 // DMD emits entries with FQN, but other implmentations
161 // (e.g. LDC) make use of directories
162 // See https://github.com/dlang/druntime/pull/2945
163 if (sourceFile.dirIndex != 0)
164 loc.directory = lp.includeDirectories[sourceFile.dirIndex - 1];
165
166 loc.file = sourceFile.file;
167 loc.line = match.line;
168 numberOfLocationsFound++;
169 }
170
171 // The state machine will not contain an entry for each
172 // address, as consecutive addresses with the same file/line
173 // are merged together to save on space, so we need to
174 // check if our address is within two addresses we get
175 // called with.
176 //
177 // Specs (DWARF v4, Section 6.2, PDF p.109) says:
178 // "We shrink it with two techniques. First, we delete from
179 // the matrix each row whose file, line, source column and
180 // discriminator information is identical with that of its
181 // predecessors.
182 if (loc.address == address)
183 update(locInfo);
184 else if (lastAddress &&
185 loc.address > lastAddress && loc.address < address)
186 update(lastLoc);
187 }
188
189 if (isEndSequence)
190 {
191 lastAddress = 0;
192 }
193 else
194 {
195 lastAddress = address;
196 lastLoc = locInfo;
197 }
198
199 return numberOfLocationsFound < locations.length;
200 }
201 );
202
203 if (numberOfLocationsFound == locations.length) return;
204 }
205 }
206
207 const(char)[] getDemangledSymbol(const(char)[] btSymbol, return ref char[1024] buffer) nothrow @nogc
208 {
209 //import core.demangle; // isn't @nogc :(
210 import bc.core.demangle : demangle;
211 import core.internal.execinfo : getMangledSymbolName;
212
213 const mangledName = getMangledSymbolName(btSymbol);
214 return !mangledName.length ? buffer[0..0] : demangle(mangledName, buffer[]);
215 // return mangledName;
216 }
217
218 struct LineNumberProgram
219 {
220 ulong unitLength;
221 ushort dwarfVersion;
222 ulong headerLength;
223 ubyte minimumInstructionLength;
224 ubyte maximumOperationsPerInstruction;
225 bool defaultIsStatement;
226 byte lineBase;
227 ubyte lineRange;
228 ubyte opcodeBase;
229 const(ubyte)[] standardOpcodeLengths;
230 Array!(const(char)[]) includeDirectories;
231 Array!SourceFile sourceFiles;
232 const(ubyte)[] program;
233 }
234
235 struct SourceFile
236 {
237 const(char)[] file;
238 size_t dirIndex;
239 }
240
241 struct LocationInfo
242 {
243 int file;
244 int line;
245 }
246
247 LineNumberProgram readLineNumberProgram(ref const(ubyte)[] data) @nogc nothrow
248 {
249 // import core.stdc.stdio : printf;
250 // printf("!my readLineNumberProgram: ");
251 // foreach (b; data[0..data.length > 256 ? 256 : $]) printf("%02X", b);
252 // printf("\n");
253
254 const originalData = data;
255
256 LineNumberProgram lp;
257
258 bool is64bitDwarf = false;
259 lp.unitLength = data.read!uint();
260 if (lp.unitLength == uint.max)
261 {
262 is64bitDwarf = true;
263 lp.unitLength = data.read!ulong();
264 }
265
266 const dwarfVersionFieldOffset = cast(size_t) (data.ptr - originalData.ptr);
267 lp.dwarfVersion = data.read!ushort();
268 debug(DwarfDebugMachine) printf("DWARF version: %d\n", lp.dwarfVersion);
269 assert(lp.dwarfVersion < 5, "DWARF v5+ not supported yet");
270
271 lp.headerLength = (is64bitDwarf ? data.read!ulong() : data.read!uint());
272
273 const minimumInstructionLengthFieldOffset = cast(size_t) (data.ptr - originalData.ptr);
274 lp.minimumInstructionLength = data.read!ubyte();
275
276 lp.maximumOperationsPerInstruction = (lp.dwarfVersion >= 4 ? data.read!ubyte() : 1);
277 lp.defaultIsStatement = (data.read!ubyte() != 0);
278 lp.lineBase = data.read!byte();
279 lp.lineRange = data.read!ubyte();
280 lp.opcodeBase = data.read!ubyte();
281
282 lp.standardOpcodeLengths = data[0 .. lp.opcodeBase - 1];
283 data = data[lp.opcodeBase - 1 .. $];
284
285 // A sequence ends with a null-byte.
286 static auto readSequence(alias ReadEntry)(ref const(ubyte)[] data)
287 {
288 alias ResultType = typeof(ReadEntry(data));
289
290 static size_t count(const(ubyte)[] data)
291 {
292 size_t count = 0;
293 while (data.length && data[0] != 0)
294 {
295 ReadEntry(data);
296 ++count;
297 }
298 return count;
299 }
300
301 const numEntries = count(data);
302
303 Array!ResultType result;
304 result.length = numEntries;
305
306 foreach (i; 0 .. numEntries)
307 result[i] = ReadEntry(data);
308
309 data = data[1 .. $]; // skip over sequence-terminating null
310
311 return result;
312 }
313
314 static const(char)[] readIncludeDirectoryEntry(ref const(ubyte)[] data)
315 {
316 const length = strlen(cast(char*) data.ptr);
317 auto result = cast(const(char)[]) data[0 .. length];
318 debug(DwarfDebugMachine) printf("dir: %.*s\n", cast(int) length, result.ptr);
319 data = data[length + 1 .. $];
320 return result;
321 }
322 lp.includeDirectories = readSequence!readIncludeDirectoryEntry(data);
323
324 static SourceFile readFileNameEntry(ref const(ubyte)[] data)
325 {
326 const length = strlen(cast(char*) data.ptr);
327 auto file = cast(const(char)[]) data[0 .. length];
328 debug(DwarfDebugMachine) printf("file: %.*s\n", cast(int) length, file.ptr);
329 data = data[length + 1 .. $];
330
331 auto dirIndex = cast(size_t) data.readULEB128();
332
333 data.readULEB128(); // last mod
334 data.readULEB128(); // file len
335
336 return SourceFile(
337 file,
338 dirIndex,
339 );
340 }
341 lp.sourceFiles = readSequence!readFileNameEntry(data);
342
343 const programStart = cast(size_t) (minimumInstructionLengthFieldOffset + lp.headerLength);
344 const programEnd = cast(size_t) (dwarfVersionFieldOffset + lp.unitLength);
345 lp.program = originalData[programStart .. programEnd];
346
347 data = originalData[programEnd .. $];
348
349 return lp;
350 }
351
352 T read(T)(ref const(ubyte)[] buffer) @nogc nothrow
353 {
354 version (X86) enum hasUnalignedLoads = true;
355 else version (X86_64) enum hasUnalignedLoads = true;
356 else enum hasUnalignedLoads = false;
357
358 static if (hasUnalignedLoads || T.alignof == 1)
359 {
360 T result = *(cast(T*) buffer.ptr);
361 }
362 else
363 {
364 import core.stdc..string : memcpy;
365 T result = void;
366 memcpy(&result, buffer.ptr, T.sizeof);
367 }
368
369 buffer = buffer[T.sizeof .. $];
370 return result;
371 }
372
373 ulong readULEB128(ref const(ubyte)[] buffer) @nogc nothrow
374 {
375 ulong val = 0;
376 uint shift = 0;
377
378 while (true)
379 {
380 ubyte b = buffer.read!ubyte();
381
382 val |= (b & 0x7f) << shift;
383 if ((b & 0x80) == 0) break;
384 shift += 7;
385 }
386
387 return val;
388 }
389
390 long readSLEB128(ref const(ubyte)[] buffer) @nogc nothrow
391 {
392 long val = 0;
393 uint shift = 0;
394 int size = 8 << 3;
395 ubyte b;
396
397 while (true)
398 {
399 b = buffer.read!ubyte();
400 val |= (b & 0x7f) << shift;
401 shift += 7;
402 if ((b & 0x80) == 0)
403 break;
404 }
405
406 if (shift < size && (b & 0x40) != 0)
407 val |= -(1 << shift);
408
409 return val;
410 }
411
412 alias RunStateMachineCallback =
413 bool delegate(size_t address, LocationInfo info, bool isEndSequence)
414 @nogc nothrow;
415
416 enum StandardOpcode : ubyte
417 {
418 extendedOp = 0,
419 copy = 1,
420 advancePC = 2,
421 advanceLine = 3,
422 setFile = 4,
423 setColumn = 5,
424 negateStatement = 6,
425 setBasicBlock = 7,
426 constAddPC = 8,
427 fixedAdvancePC = 9,
428 setPrologueEnd = 10,
429 setEpilogueBegin = 11,
430 setISA = 12,
431 }
432
433 enum ExtendedOpcode : ubyte
434 {
435 endSequence = 1,
436 setAddress = 2,
437 defineFile = 3,
438 setDiscriminator = 4,
439 }
440
441 struct StateMachine
442 {
443 size_t address = 0;
444 uint operationIndex = 0;
445 uint fileIndex = 1;
446 uint line = 1;
447 uint column = 0;
448 uint isa = 0;
449 uint discriminator = 0;
450 bool isStatement;
451 bool isBasicBlock = false;
452 bool isEndSequence = false;
453 bool isPrologueEnd = false;
454 bool isEpilogueBegin = false;
455 }
456
457 bool runStateMachine(ref const(LineNumberProgram) lp, scope RunStateMachineCallback callback) @nogc nothrow
458 {
459 StateMachine machine;
460 machine.isStatement = lp.defaultIsStatement;
461
462 const(ubyte)[] program = lp.program;
463 while (program.length > 0)
464 {
465 size_t advanceAddressAndOpIndex(size_t operationAdvance)
466 {
467 const addressIncrement = lp.minimumInstructionLength * ((machine.operationIndex + operationAdvance) / lp.maximumOperationsPerInstruction);
468 machine.address += addressIncrement;
469 machine.operationIndex = (machine.operationIndex + operationAdvance) % lp.maximumOperationsPerInstruction;
470 return addressIncrement;
471 }
472
473 ubyte opcode = program.read!ubyte();
474 if (opcode < lp.opcodeBase)
475 {
476 switch (opcode) with (StandardOpcode)
477 {
478 case extendedOp:
479 size_t len = cast(size_t) program.readULEB128();
480 ubyte eopcode = program.read!ubyte();
481
482 switch (eopcode) with (ExtendedOpcode)
483 {
484 case endSequence:
485 machine.isEndSequence = true;
486 debug(DwarfDebugMachine) printf("endSequence 0x%zx\n", machine.address);
487 if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), true)) return true;
488 machine = StateMachine.init;
489 machine.isStatement = lp.defaultIsStatement;
490 break;
491
492 case setAddress:
493 size_t address = program.read!size_t();
494 debug(DwarfDebugMachine) printf("setAddress 0x%zx\n", address);
495 machine.address = address;
496 machine.operationIndex = 0;
497 break;
498
499 case defineFile: // TODO: add proper implementation
500 debug(DwarfDebugMachine) printf("defineFile\n");
501 program = program[len - 1 .. $];
502 break;
503
504 case setDiscriminator:
505 const discriminator = cast(uint) program.readULEB128();
506 debug(DwarfDebugMachine) printf("setDiscriminator %d\n", discriminator);
507 machine.discriminator = discriminator;
508 break;
509
510 default:
511 // unknown opcode
512 debug(DwarfDebugMachine) printf("unknown extended opcode %d\n", cast(int) eopcode);
513 program = program[len - 1 .. $];
514 break;
515 }
516
517 break;
518
519 case copy:
520 debug(DwarfDebugMachine) printf("copy 0x%zx\n", machine.address);
521 if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), false)) return true;
522 machine.isBasicBlock = false;
523 machine.isPrologueEnd = false;
524 machine.isEpilogueBegin = false;
525 machine.discriminator = 0;
526 break;
527
528 case advancePC:
529 const operationAdvance = cast(size_t) readULEB128(program);
530 advanceAddressAndOpIndex(operationAdvance);
531 debug(DwarfDebugMachine) printf("advancePC %d to 0x%zx\n", cast(int) operationAdvance, machine.address);
532 break;
533
534 case advanceLine:
535 long ad = readSLEB128(program);
536 machine.line += ad;
537 debug(DwarfDebugMachine) printf("advanceLine %d to %d\n", cast(int) ad, cast(int) machine.line);
538 break;
539
540 case setFile:
541 uint index = cast(uint) readULEB128(program);
542 debug(DwarfDebugMachine) printf("setFile to %d\n", cast(int) index);
543 machine.fileIndex = index;
544 break;
545
546 case setColumn:
547 uint col = cast(uint) readULEB128(program);
548 debug(DwarfDebugMachine) printf("setColumn %d\n", cast(int) col);
549 machine.column = col;
550 break;
551
552 case negateStatement:
553 debug(DwarfDebugMachine) printf("negateStatement\n");
554 machine.isStatement = !machine.isStatement;
555 break;
556
557 case setBasicBlock:
558 debug(DwarfDebugMachine) printf("setBasicBlock\n");
559 machine.isBasicBlock = true;
560 break;
561
562 case constAddPC:
563 const operationAdvance = (255 - lp.opcodeBase) / lp.lineRange;
564 advanceAddressAndOpIndex(operationAdvance);
565 debug(DwarfDebugMachine) printf("constAddPC 0x%zx\n", machine.address);
566 break;
567
568 case fixedAdvancePC:
569 const add = program.read!ushort();
570 machine.address += add;
571 machine.operationIndex = 0;
572 debug(DwarfDebugMachine) printf("fixedAdvancePC %d to 0x%zx\n", cast(int) add, machine.address);
573 break;
574
575 case setPrologueEnd:
576 machine.isPrologueEnd = true;
577 debug(DwarfDebugMachine) printf("setPrologueEnd\n");
578 break;
579
580 case setEpilogueBegin:
581 machine.isEpilogueBegin = true;
582 debug(DwarfDebugMachine) printf("setEpilogueBegin\n");
583 break;
584
585 case setISA:
586 machine.isa = cast(uint) readULEB128(program);
587 debug(DwarfDebugMachine) printf("setISA %d\n", cast(int) machine.isa);
588 break;
589
590 default:
591 debug(DwarfDebugMachine) printf("unknown opcode %d\n", cast(int) opcode);
592 return false;
593 }
594 }
595 else
596 {
597 opcode -= lp.opcodeBase;
598 const operationAdvance = opcode / lp.lineRange;
599 const addressIncrement = advanceAddressAndOpIndex(operationAdvance);
600 const lineIncrement = lp.lineBase + (opcode % lp.lineRange);
601 machine.line += lineIncrement;
602
603 debug (DwarfDebugMachine)
604 printf("special %d %d to 0x%zx line %d\n", cast(int) addressIncrement,
605 cast(int) lineIncrement, machine.address, machine.line);
606
607 if (!callback(machine.address, LocationInfo(machine.fileIndex, machine.line), false)) return true;
608
609 machine.isBasicBlock = false;
610 machine.isPrologueEnd = false;
611 machine.isEpilogueBegin = false;
612 machine.discriminator = 0;
613 }
614 }
615
616 return true;
617 }
618
619 struct Location
620 {
621 const(char)[] file = null;
622 const(char)[] directory = null;
623 int line = -1;
624 size_t address;
625 }
626
627 // See: module rt.util.container.array;
628 struct Array(T)
629 {
630 nothrow:
631 @disable this(this);
632
633 ~this()
634 {
635 reset();
636 }
637
638 void reset()
639 {
640 length = 0;
641 }
642
643 @property size_t length() const
644 {
645 return _length;
646 }
647
648 @property void length(size_t nlength)
649 {
650 import core.checkedint : mulu;
651
652 bool overflow = false;
653 size_t reqsize = mulu(T.sizeof, nlength, overflow);
654 if (!overflow)
655 {
656 if (nlength < _length)
657 foreach (ref val; _ptr[nlength .. _length]) .destroy(val);
658 _ptr = cast(T*).xrealloc(_ptr, reqsize);
659 if (nlength > _length)
660 foreach (ref val; _ptr[_length .. nlength]) .initialize(val);
661 _length = nlength;
662 }
663 else
664 onOutOfMemoryErrorNoGC();
665
666 }
667
668 @property bool empty() const
669 {
670 return !length;
671 }
672
673 @property ref inout(T) front() inout
674 in { assert(!empty); }
675 do
676 {
677 return _ptr[0];
678 }
679
680 @property ref inout(T) back() inout
681 in { assert(!empty); }
682 do
683 {
684 return _ptr[_length - 1];
685 }
686
687 ref inout(T) opIndex(size_t idx) inout
688 in { assert(idx < length); }
689 do
690 {
691 return _ptr[idx];
692 }
693
694 inout(T)[] opSlice() inout
695 {
696 return _ptr[0 .. _length];
697 }
698
699 inout(T)[] opSlice(size_t a, size_t b) inout
700 in { assert(a < b && b <= length); }
701 do
702 {
703 return _ptr[a .. b];
704 }
705
706 alias length opDollar;
707
708 void insertBack()(auto ref T val)
709 {
710 import core.checkedint : addu;
711
712 bool overflow = false;
713 size_t newlength = addu(length, 1, overflow);
714 if (!overflow)
715 {
716 length = newlength;
717 back = val;
718 }
719 else
720 onOutOfMemoryErrorNoGC();
721 }
722
723 void popBack()
724 {
725 length = length - 1;
726 }
727
728 void remove(size_t idx)
729 in { assert(idx < length); }
730 do
731 {
732 foreach (i; idx .. length - 1)
733 _ptr[i] = _ptr[i+1];
734 popBack();
735 }
736
737 void swap(ref Array other)
738 {
739 auto ptr = _ptr;
740 _ptr = other._ptr;
741 other._ptr = ptr;
742 immutable len = _length;
743 _length = other._length;
744 other._length = len;
745 }
746
747 invariant
748 {
749 assert(!_ptr == !_length);
750 }
751
752 private:
753 T* _ptr;
754 size_t _length;
755 }
756
757 import core.stdc.stdlib : malloc, realloc, free;
758
759 // See: rt.util.container.common
760 void* xrealloc(void* ptr, size_t sz) nothrow @nogc
761 {
762 import core.exception;
763
764 if (!sz) { .free(ptr); return null; }
765 if (auto nptr = .realloc(ptr, sz)) return nptr;
766 .free(ptr); onOutOfMemoryErrorNoGC();
767 assert(0);
768 }
769
770 void destroy(T)(ref T t) if (is(T == struct) && dtorIsNothrow!T)
771 {
772 scope (failure) assert(0); // nothrow hack
773 object.destroy(t);
774 }
775
776 void destroy(T)(ref T t) if (!is(T == struct))
777 {
778 t = T.init;
779 }
780
781 void initialize(T)(ref T t) if (is(T == struct))
782 {
783 import core.stdc..string;
784 static if (__traits(isPOD, T)) // implies !hasElaborateAssign!T && !hasElaborateDestructor!T
785 t = T.init;
786 else static if (__traits(isZeroInit, T))
787 memset(&t, 0, T.sizeof);
788 else
789 memcpy(&t, typeid(T).initializer().ptr, T.sizeof);
790 }
791
792 void initialize(T)(ref T t) if (!is(T == struct))
793 {
794 t = T.init;
795 }