1 /**
2  * Generate debug info in the CV4 debug format.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/tocsym.d, _tocvdebug.d)
8  * Documentation:  https://dlang.org/phobos/dmd_tocvdebug.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/tocvdebug.d
10  */
11 
12 module dmd.tocvdebug;
13 
14 import core.stdc.stdio;
15 import core.stdc.string;
16 import core.stdc.stddef;
17 import core.stdc.stdlib;
18 import core.stdc.time;
19 
20 import dmd.root.array;
21 import dmd.root.rmem;
22 
23 import dmd.aggregate;
24 import dmd.astenums;
25 import dmd.dclass;
26 import dmd.declaration;
27 import dmd.denum;
28 import dmd.dmodule;
29 import dmd.dsymbol;
30 import dmd.dstruct;
31 import dmd.dtemplate;
32 import dmd.func;
33 import dmd.globals;
34 import dmd.id;
35 import dmd.mtype;
36 import dmd.target;
37 import dmd.toctype;
38 import dmd.visitor;
39 
40 import dmd.backend.cc;
41 import dmd.backend.cdef;
42 import dmd.backend.cgcv;
43 import dmd.backend.code;
44 import dmd.backend.cv4;
45 import dmd.backend.dlist;
46 import dmd.backend.dt;
47 import dmd.backend.global;
48 import dmd.backend.obj;
49 import dmd.backend.oper;
50 import dmd.backend.ty;
51 import dmd.backend.type;
52 
53 /* The CV4 debug format is defined in:
54  *      "CV4 Symbolic Debug Information Specification"
55  *      rev 3.1 March 5, 1993
56  *      Languages Business Unit
57  *      Microsoft
58  */
59 
60 /******************************
61  * CV4 pg. 25
62  * Convert D visibility attribute to cv attribute.
63  */
64 
65 uint visibilityToCVAttr(Visibility.Kind vis) pure nothrow @safe @nogc
66 {
67     uint attribute;
68 
69     final switch (vis)
70     {
71         case Visibility.Kind.private_:       attribute = 1;  break;
72         case Visibility.Kind.package_:       attribute = 2;  break;
73         case Visibility.Kind.protected_:     attribute = 2;  break;
74         case Visibility.Kind.public_:        attribute = 3;  break;
75         case Visibility.Kind.export_:        attribute = 3;  break;
76 
77         case Visibility.Kind.undefined:
78         case Visibility.Kind.none:
79             //printf("vis = %d\n", vis);
80             assert(0);
81     }
82     return attribute;
83 }
84 
85 uint cv4_memfunctypidx(FuncDeclaration fd)
86 {
87     //printf("cv4_memfunctypidx(fd = '%s')\n", fd.toChars());
88 
89     type *t = Type_toCtype(fd.type);
90     AggregateDeclaration ad = fd.isMemberLocal();
91     if (!ad)
92          return cv4_typidx(t);
93 
94     // It's a member function, which gets a special type record
95 
96     const idx_t thisidx = fd.isStatic()
97                 ? dttab4[TYvoid]
98                 : (ad.handleType() ? cv4_typidx(Type_toCtype(ad.handleType())) : 0);
99     assert(thisidx);
100 
101     uint nparam;
102     const idx_t paramidx = cv4_arglist(t,&nparam);
103 
104     const ubyte call = cv4_callconv(t);
105 
106     switch (config.fulltypes)
107     {
108         case CV4:
109         {
110             debtyp_t* d = debtyp_alloc(18);
111             ubyte *p = &d.data[0];
112             TOWORD(p,LF_MFUNCTION);
113             TOWORD(p + 2,cv4_typidx(t.Tnext));
114             TOWORD(p + 4,cv4_typidx(Type_toCtype(ad.type)));
115             TOWORD(p + 6,thisidx);
116             p[8] = call;
117             p[9] = 0;                               // reserved
118             TOWORD(p + 10,nparam);
119             TOWORD(p + 12,paramidx);
120             TOLONG(p + 14,0);                       // thisadjust
121             return cv_debtyp(d);
122         }
123         case CV8:
124         {
125             debtyp_t* d = debtyp_alloc(26);
126             ubyte *p = &d.data[0];
127             TOWORD(p,0x1009);
128             TOLONG(p + 2,cv4_typidx(t.Tnext));
129             TOLONG(p + 6,cv4_typidx(Type_toCtype(ad.type)));
130             TOLONG(p + 10,thisidx);
131             p[14] = call;
132             p[15] = 0;                               // reserved
133             TOWORD(p + 16,nparam);
134             TOLONG(p + 18,paramidx);
135             TOLONG(p + 22,0);                       // thisadjust
136             return cv_debtyp(d);
137         }
138         default:
139             assert(0);
140     }
141 }
142 
143 enum CV4_NAMELENMAX = 0x3b9f;                   // found by trial and error
144 enum CV8_NAMELENMAX = 0xffff;                   // length record is 16-bit only
145 
146 uint cv4_Denum(EnumDeclaration e)
147 {
148     //dbg_printf("cv4_Denum(%s)\n", e.toChars());
149     const uint property = (!e.members || !e.memtype || !e.memtype.isintegral())
150         ? 0x80               // enum is forward referenced or non-integer
151         : 0;
152 
153     // Compute the number of fields, and the length of the fieldlist record
154     CvFieldList mc = CvFieldList(0, 0);
155     if (!property)
156     {
157         for (size_t i = 0; i < e.members.length; i++)
158         {
159             EnumMember sf = (*e.members)[i].isEnumMember();
160             if (!sf)
161                 continue;
162             const value = sf.value().toInteger();
163 
164             // store only member's simple name
165             uint len = 4 + cv4_numericbytes(cast(uint)value) + cv_stringbytes(sf.toChars());
166 
167             len = cv_align(null, len);
168             mc.count(len);
169 
170         }
171     }
172 
173     const id = e.toPrettyChars();
174     uint len;
175     debtyp_t *d;
176     const uint memtype = e.memtype ? cv4_typidx(Type_toCtype(e.memtype)) : 0;
177     switch (config.fulltypes)
178     {
179         case CV8:
180             len = 14;
181             d = debtyp_alloc(len + cv_stringbytes(id));
182             TOWORD(d.data.ptr,LF_ENUM_V3);
183             TOLONG(d.data.ptr + 6,memtype);
184             TOWORD(d.data.ptr + 4,property);
185             len += cv_namestring(d.data.ptr + len,id);
186             break;
187 
188         case CV4:
189             len = 10;
190             d = debtyp_alloc(len + cv_stringbytes(id));
191             TOWORD(d.data.ptr,LF_ENUM);
192             TOWORD(d.data.ptr + 4,memtype);
193             TOWORD(d.data.ptr + 8,property);
194             len += cv_namestring(d.data.ptr + len,id);
195             break;
196 
197         default:
198             assert(0);
199     }
200     const length_save = d.length;
201     d.length = 0;                      // so cv_debtyp() will allocate new
202     const idx_t typidx = cv_debtyp(d);
203     d.length = length_save;            // restore length
204 
205     TOWORD(d.data.ptr + 2, mc.nfields);
206 
207     uint fieldlist = 0;
208     if (!property)                      // if forward reference, then fieldlist is 0
209     {
210         // Generate fieldlist type record
211         mc.alloc();
212 
213         // And fill it in
214         for (size_t i = 0; i < e.members.length; i++)
215         {
216             EnumMember sf = (*e.members)[i].isEnumMember();
217             if (!sf)
218                 continue;
219 
220             ubyte* p = mc.writePtr();
221             dinteger_t value = sf.value().toInteger();
222             TOWORD(p, (config.fulltypes == CV8) ? LF_ENUMERATE_V3 : LF_ENUMERATE);
223             uint attribute = 0;
224             TOWORD(p + 2, attribute);
225             cv4_storenumeric(p + 4,cast(uint)value);
226             uint j = 4 + cv4_numericbytes(cast(uint)value);
227             // store only member's simple name
228             j += cv_namestring(p + j, sf.toChars());
229             j = cv_align(p + j, j);
230             mc.written(j);
231             // If enum is not a member of a class, output enum members as constants
232     //      if (!isclassmember(s))
233     //      {
234     //          cv4_outsym(sf);
235     //      }
236         }
237         fieldlist = mc.debtyp();
238     }
239 
240     if (config.fulltypes == CV8)
241         TOLONG(d.data.ptr + 10,fieldlist);
242     else
243         TOWORD(d.data.ptr + 6,fieldlist);
244 
245 //    cv4_outsym(s);
246     return typidx;
247 }
248 
249 /*************************************
250  * Align and pad.
251  * Returns:
252  *      aligned count
253  */
254 uint cv_align(ubyte *p, uint n)
255 {
256     if (config.fulltypes == CV8)
257     {
258         if (p)
259         {
260             uint npad = -n & 3;
261             while (npad)
262             {
263                 *p = cast(ubyte)(0xF0 + npad);
264                 ++p;
265                 --npad;
266             }
267         }
268         n = (n + 3) & ~3;
269     }
270     return n;
271 }
272 
273 /*************************************
274  * write a UDT record to the object file
275  * Params:
276  *      id = name of user defined type
277  *      typidx = type index
278  */
279 void cv_udt(const char* id, uint typidx)
280 {
281     if (config.fulltypes == CV8)
282         return cv8_udt(id, typidx);
283 
284     const len = strlen(id);
285     ubyte *debsym = cast(ubyte *) alloca(39 + IDOHD + len);
286 
287     // Output a 'user-defined type' for the tag name
288     TOWORD(debsym + 2,S_UDT);
289     TOIDX(debsym + 4,typidx);
290     uint length = 2 + 2 + cgcv.sz_idx;
291     length += cv_namestring(debsym + length,id);
292     TOWORD(debsym,length - 2);
293 
294     assert(length <= 40 + len);
295     objmod.write_bytes(SegData[DEBSYM],debsym[0 .. length]);
296 }
297 
298 /* ==================================================================== */
299 
300 /****************************
301  * Emit symbolic debug info in CV format.
302  */
303 
304 void toDebug(EnumDeclaration ed)
305 {
306     if (target.os != Target.OS.Windows)
307         return;
308 
309     //printf("EnumDeclaration::toDebug('%s')\n", ed.toChars());
310 
311     assert(config.fulltypes >= CV4);
312 
313     // If it is a member, it is handled by cvMember()
314     if (!ed.isMember())
315     {
316         const id = ed.toPrettyChars(true);
317         const idx_t typidx = cv4_Denum(ed);
318         cv_udt(id, typidx);
319     }
320 }
321 
322 /****************************
323  * Helper struct for field list records LF_FIELDLIST/LF_FIELDLIST_V2
324  *
325  * if the size exceeds the maximum length of a record, the last entry
326  * is an LF_INDEX entry with the type index pointing to the next field list record
327  *
328  * Processing is done in two phases:
329  *
330  * Phase 1: computing the size of the field list and distributing it over multiple records
331  *  - construct CvFieldList with some precalculated field count/length
332  *  - for each field, call count(length of field)
333  *
334  * Phase 2: write the actual data
335  *  - call alloc() to allocate debtyp's
336  *  - for each field,
337  *    - call writePtr() to get a pointer into the current debtyp
338  *    - fill memory with field data
339  *    - call written(length of field)
340  *  - call debtyp() to create type records and return the index of the first one
341  */
342 struct CvFieldList
343 {
344     // one LF_FIELDLIST record
345     static struct FLChunk
346     {
347         uint length;    // accumulated during "count" phase
348 
349         debtyp_t *dt;
350         uint writepos;  // write position in dt
351     }
352 
353     uint nfields;
354     uint writeIndex;
355     Array!FLChunk fieldLists;
356 
357     const uint fieldLenMax;
358     const uint fieldIndexLen;
359 
360     const bool canSplitList;
361 
362     this(uint fields, uint len) scope
363     {
364         canSplitList = config.fulltypes == CV8; // optlink bails out with LF_INDEX
365         fieldIndexLen = canSplitList ? (config.fulltypes == CV8 ? 2 + 2 + 4 : 2 + 2) : 0;
366         fieldLenMax = (config.fulltypes == CV8 ? CV8_NAMELENMAX : CV4_NAMELENMAX) - fieldIndexLen;
367 
368         assert(len < fieldLenMax);
369         nfields = fields;
370         fieldLists.push(FLChunk(2 + len));
371     }
372 
373     void count(uint n)
374     {
375         if (n)
376         {
377             nfields++;
378             assert(n < fieldLenMax);
379             if (fieldLists[$-1].length + n > fieldLenMax)
380                 fieldLists.push(FLChunk(2 + n));
381             else
382                 fieldLists[$-1].length += n;
383         }
384     }
385 
386     void alloc()
387     {
388         foreach (i, ref fld; fieldLists)
389         {
390             fld.dt = debtyp_alloc(fld.length + (i < fieldLists.length - 1 ? fieldIndexLen : 0));
391             TOWORD(fld.dt.data.ptr, config.fulltypes == CV8 ? LF_FIELDLIST_V2 : LF_FIELDLIST);
392             fld.writepos = 2;
393         }
394     }
395 
396     ubyte* writePtr()
397     {
398         assert(writeIndex < fieldLists.length);
399         auto fld = &fieldLists[writeIndex];
400         if (fld.writepos >= fld.length)
401         {
402             assert(fld.writepos == fld.length);
403             if (writeIndex < fieldLists.length - 1) // if false, all further attempts must not actually write any data
404             {
405                 writeIndex++;
406                 fld++;
407             }
408         }
409         return fld.dt.data.ptr + fld.writepos;
410     }
411 
412     void written(uint n)
413     {
414         assert(fieldLists[writeIndex].writepos + n <= fieldLists[writeIndex].length);
415         fieldLists[writeIndex].writepos += n;
416     }
417 
418     idx_t debtyp()
419     {
420         idx_t typidx;
421         auto numCreate = canSplitList ? fieldLists.length : 1;
422         for(auto i = numCreate; i > 0; --i)
423         {
424             auto fld = &fieldLists[i - 1];
425             if (typidx)
426             {
427                 ubyte* p = fld.dt.data.ptr + fld.writepos;
428                 if (config.fulltypes == CV8)
429                 {
430                     TOWORD (p, LF_INDEX_V2);
431                     TOWORD (p + 2, 0); // padding
432                     TOLONG (p + 4, typidx);
433                 }
434                 else
435                 {
436                     TOWORD (p, LF_INDEX);
437                     TOWORD (p + 2, typidx);
438                 }
439             }
440             typidx = cv_debtyp(fld.dt);
441         }
442         return typidx;
443     }
444 }
445 
446 // Lambda function
447 int cv_mem_count(Dsymbol s, void* ctx)
448 {
449     auto pmc = cast(CvFieldList *) ctx;
450     int nwritten = cvMember(s, null);
451     pmc.count(nwritten);
452     return 0;
453 }
454 
455 // Lambda function
456 int cv_mem_p(Dsymbol s, void* ctx)
457 {
458     auto pmc = cast(CvFieldList *) ctx;
459     ubyte *p = pmc.writePtr();
460     uint len = cvMember(s, p);
461     pmc.written(len);
462     return 0;
463 }
464 
465 
466 void toDebug(StructDeclaration sd)
467 {
468     if (target.os != Target.OS.Windows)
469         return;
470 
471     idx_t typidx1 = 0;
472 
473     //printf("StructDeclaration::toDebug('%s')\n", sd.toChars());
474 
475     assert(config.fulltypes >= CV4);
476     if (sd.isAnonymous())
477         return /*0*/;
478 
479     if (typidx1)                 // if reference already generated
480         return /*typidx1*/;      // use already existing reference
481 
482     targ_size_t size;
483     uint property = 0;
484     if (!sd.members)
485     {
486         size = 0;
487         property |= 0x80;               // forward reference
488     }
489     else
490         size = sd.structsize;
491 
492     if (sd.parent.isAggregateDeclaration()) // if class is nested
493         property |= 8;
494 //    if (st.Sctor || st.Sdtor)
495 //      property |= 2;          // class has ctors and/or dtors
496 //    if (st.Sopoverload)
497 //      property |= 4;          // class has overloaded operators
498 //    if (st.Scastoverload)
499 //      property |= 0x40;               // class has casting methods
500 //    if (st.Sopeq && !(st.Sopeq.Sfunc.Fflags & Fnodebug))
501 //      property |= 0x20;               // class has overloaded assignment
502 
503     const char *id = sd.toPrettyChars(true);
504 
505     uint leaf = sd.isUnionDeclaration() ? LF_UNION : LF_STRUCTURE;
506     if (config.fulltypes == CV8)
507         leaf = leaf == LF_UNION ? LF_UNION_V3 : LF_STRUCTURE_V3;
508 
509     uint numidx;
510     final switch (leaf)
511     {
512         case LF_UNION:        numidx = 8;       break;
513         case LF_UNION_V3:     numidx = 10;      break;
514         case LF_STRUCTURE:    numidx = 12;      break;
515         case LF_STRUCTURE_V3: numidx = 18;      break;
516     }
517 
518     const len1 = numidx + cv4_numericbytes(cast(uint)size);
519     debtyp_t *d = debtyp_alloc(len1 + cv_stringbytes(id));
520     cv4_storenumeric(d.data.ptr + numidx, cast(uint)size);
521     cv_namestring(d.data.ptr + len1, id);
522 
523     if (leaf == LF_STRUCTURE)
524     {
525         TOWORD(d.data.ptr + 8,0);          // dList
526         TOWORD(d.data.ptr + 10,0);         // vshape is 0 (no virtual functions)
527     }
528     else if (leaf == LF_STRUCTURE_V3)
529     {
530         TOLONG(d.data.ptr + 10,0);         // dList
531         TOLONG(d.data.ptr + 14,0);         // vshape is 0 (no virtual functions)
532     }
533     TOWORD(d.data.ptr,leaf);
534 
535     // Assign a number to prevent infinite recursion if a struct member
536     // references the same struct.
537     const length_save = d.length;
538     d.length = 0;                      // so cv_debtyp() will allocate new
539     const idx_t typidx = cv_debtyp(d);
540     d.length = length_save;            // restore length
541 
542     if (!sd.members)                       // if reference only
543     {
544         if (config.fulltypes == CV8)
545         {
546             TOWORD(d.data.ptr + 2,0);          // count: number of fields is 0
547             TOLONG(d.data.ptr + 6,0);          // field list is 0
548             TOWORD(d.data.ptr + 4,property);
549         }
550         else
551         {
552             TOWORD(d.data.ptr + 2,0);          // count: number of fields is 0
553             TOWORD(d.data.ptr + 4,0);          // field list is 0
554             TOWORD(d.data.ptr + 6,property);
555         }
556         return /*typidx*/;
557     }
558 
559     // Compute the number of fields and the length of the fieldlist record
560     CvFieldList mc = CvFieldList(0, 0);
561     for (size_t i = 0; i < sd.members.length; i++)
562     {
563         Dsymbol s = (*sd.members)[i];
564         s.apply(&cv_mem_count, &mc);
565     }
566     const uint nfields = mc.nfields;
567 
568     // Generate fieldlist type record
569     mc.alloc();
570     if (nfields)
571     {
572         for (size_t i = 0; i < sd.members.length; i++)
573         {
574             Dsymbol s = (*sd.members)[i];
575             s.apply(&cv_mem_p, &mc);
576         }
577     }
578 
579     //dbg_printf("fnamelen = %d, p-dt.data.ptr = %d\n",fnamelen,p-dt.data.ptr);
580     const idx_t fieldlist = mc.debtyp();
581 
582     TOWORD(d.data.ptr + 2, nfields);
583     if (config.fulltypes == CV8)
584     {
585         TOWORD(d.data.ptr + 4,property);
586         TOLONG(d.data.ptr + 6,fieldlist);
587     }
588     else
589     {
590         TOWORD(d.data.ptr + 4,fieldlist);
591         TOWORD(d.data.ptr + 6,property);
592     }
593 
594 //    cv4_outsym(s);
595 
596     cv_udt(id, typidx);
597 
598 //    return typidx;
599 }
600 
601 
602 void toDebug(ClassDeclaration cd)
603 {
604     if (target.os != Target.OS.Windows)
605         return;
606 
607     idx_t typidx1 = 0;
608 
609     //printf("ClassDeclaration::toDebug('%s')\n", cd.toChars());
610 
611     assert(config.fulltypes >= CV4);
612     if (cd.isAnonymous())
613         return /*0*/;
614 
615     if (typidx1)                 // if reference already generated
616         return /*typidx1*/;      // use already existing reference
617 
618     targ_size_t size;
619     uint property = 0;
620     if (!cd.members)
621     {
622         size = 0;
623         property |= 0x80;               // forward reference
624     }
625     else
626         size = cd.structsize;
627 
628     if (cd.parent.isAggregateDeclaration()) // if class is nested
629         property |= 8;
630     if (cd.ctor || cd.dtor)
631         property |= 2;          // class has ctors and/or dtors
632 //    if (st.Sopoverload)
633 //      property |= 4;          // class has overloaded operators
634 //    if (st.Scastoverload)
635 //      property |= 0x40;               // class has casting methods
636 //    if (st.Sopeq && !(st.Sopeq.Sfunc.Fflags & Fnodebug))
637 //      property |= 0x20;               // class has overloaded assignment
638 
639     const id = cd.isCPPinterface() ? cd.ident.toChars() : cd.toPrettyChars(true);
640     const uint leaf = config.fulltypes == CV8 ? LF_CLASS_V3 : LF_CLASS;
641 
642     const uint numidx = (leaf == LF_CLASS_V3) ? 18 : 12;
643     const uint len1 = numidx + cv4_numericbytes(cast(uint)size);
644     debtyp_t *d = debtyp_alloc(len1 + cv_stringbytes(id));
645     cv4_storenumeric(d.data.ptr + numidx, cast(uint)size);
646     cv_namestring(d.data.ptr + len1, id);
647 
648     idx_t vshapeidx = 0;
649     if (1)
650     {
651         const size_t dim = cd.vtbl.length;              // number of virtual functions
652         if (dim)
653         {   // 4 bits per descriptor
654             debtyp_t *vshape = debtyp_alloc(cast(uint)(4 + (dim + 1) / 2));
655             TOWORD(vshape.data.ptr,LF_VTSHAPE);
656             TOWORD(vshape.data.ptr + 2, cast(uint)dim);
657 
658             size_t n = 0;
659             ubyte descriptor = 0;
660             for (size_t i = 0; i < cd.vtbl.length; i++)
661             {
662                 //if (intsize == 4)
663                     descriptor |= 5;
664                 vshape.data.ptr[4 + n / 2] = descriptor;
665                 descriptor <<= 4;
666                 n++;
667             }
668             vshapeidx = cv_debtyp(vshape);
669         }
670     }
671     if (leaf == LF_CLASS)
672     {
673         TOWORD(d.data.ptr + 8,0);          // dList
674         TOWORD(d.data.ptr + 10,vshapeidx);
675     }
676     else if (leaf == LF_CLASS_V3)
677     {
678         TOLONG(d.data.ptr + 10,0);         // dList
679         TOLONG(d.data.ptr + 14,vshapeidx);
680     }
681     TOWORD(d.data.ptr,leaf);
682 
683     // Assign a number to prevent infinite recursion if a struct member
684     // references the same struct.
685     const length_save = d.length;
686     d.length = 0;                      // so cv_debtyp() will allocate new
687     const idx_t typidx = cv_debtyp(d);
688     d.length = length_save;            // restore length
689 
690     if (!cd.members)                       // if reference only
691     {
692         if (leaf == LF_CLASS_V3)
693         {
694             TOWORD(d.data.ptr + 2,0);          // count: number of fields is 0
695             TOLONG(d.data.ptr + 6,0);          // field list is 0
696             TOWORD(d.data.ptr + 4,property);
697         }
698         else
699         {
700             TOWORD(d.data.ptr + 2,0);          // count: number of fields is 0
701             TOWORD(d.data.ptr + 4,0);          // field list is 0
702             TOWORD(d.data.ptr + 6,property);
703         }
704         return /*typidx*/;
705     }
706 
707     // Compute the number of fields and the length of the fieldlist record
708     CvFieldList mc = CvFieldList(0, 0);
709 
710     /* Adding in the base classes causes VS 2010 debugger to refuse to display any
711      * of the fields. I have not been able to determine why.
712      * (Could it be because the base class is "forward referenced"?)
713      * It does work with VS 2012.
714      */
715     bool addInBaseClasses = true;
716     if (addInBaseClasses)
717     {
718         // Add in base classes
719         for (size_t i = 0; i < cd.baseclasses.length; i++)
720         {
721             const bc = (*cd.baseclasses)[i];
722             const uint elementlen = 4 + cgcv.sz_idx + cv4_numericbytes(bc.offset);
723             mc.count(cv_align(null, elementlen));
724         }
725     }
726 
727     for (size_t i = 0; i < cd.members.length; i++)
728     {
729         Dsymbol s = (*cd.members)[i];
730         s.apply(&cv_mem_count, &mc);
731     }
732     const uint nfields = mc.nfields;
733 
734     TOWORD(d.data.ptr + 2, nfields);
735 
736     // Generate fieldlist type record
737     mc.alloc();
738 
739     if (nfields)        // if we didn't overflow
740     {
741         if (addInBaseClasses)
742         {
743             ubyte* base = mc.writePtr();
744             ubyte* p = base;
745 
746             // Add in base classes
747             for (size_t i = 0; i < cd.baseclasses.length; i++)
748             {
749                 BaseClass *bc = (*cd.baseclasses)[i];
750                 const idx_t typidx2 = cv4_typidx(Type_toCtype(bc.sym.type).Tnext);
751                 const uint attribute = visibilityToCVAttr(Visibility.Kind.public_);
752 
753                 uint elementlen;
754                 final switch (config.fulltypes)
755                 {
756                     case CV8:
757                         TOWORD(p, LF_BCLASS_V2);
758                         TOWORD(p + 2,attribute);
759                         TOLONG(p + 4,typidx2);
760                         elementlen = 8;
761                         break;
762 
763                     case CV4:
764                         TOWORD(p, LF_BCLASS);
765                         TOWORD(p + 2,typidx2);
766                         TOWORD(p + 4,attribute);
767                         elementlen = 6;
768                         break;
769                 }
770 
771                 cv4_storenumeric(p + elementlen, bc.offset);
772                 elementlen += cv4_numericbytes(bc.offset);
773                 p += cv_align(p + elementlen, elementlen);
774             }
775             mc.written(cast(uint)(p - base));
776         }
777 
778         foreach(s; (*cd.members)[])
779             s.apply(&cv_mem_p, &mc);
780     }
781 
782     const idx_t fieldlist = mc.debtyp();
783 
784     if (config.fulltypes == CV8)
785     {
786         TOWORD(d.data.ptr + 4,property);
787         TOLONG(d.data.ptr + 6,fieldlist);
788     }
789     else
790     {
791         TOWORD(d.data.ptr + 4,fieldlist);
792         TOWORD(d.data.ptr + 6,property);
793     }
794 
795 //    cv4_outsym(s);
796 
797     cv_udt(id, typidx);
798 
799 //    return typidx;
800 }
801 
802 private uint writeField(ubyte* p, const char* id, uint attr, uint typidx, uint offset)
803 {
804     if (config.fulltypes == CV8)
805     {
806         TOWORD(p,LF_MEMBER_V3);
807         TOWORD(p + 2,attr);
808         TOLONG(p + 4,typidx);
809         cv4_storesignednumeric(p + 8, offset);
810         uint len = 8 + cv4_signednumericbytes(offset);
811         len += cv_namestring(p + len, id);
812         return cv_align(p + len, len);
813     }
814     else
815     {
816         TOWORD(p,LF_MEMBER);
817         TOWORD(p + 2,typidx);
818         TOWORD(p + 4,attr);
819         cv4_storesignednumeric(p + 6, offset);
820         uint len = 6 + cv4_signednumericbytes(offset);
821         return len + cv_namestring(p + len, id);
822     }
823 }
824 
825 void toDebugClosure(Symbol* closstru)
826 {
827     if (target.os != Target.OS.Windows)
828         return;
829 
830     //printf("toDebugClosure('%s')\n", fd.toChars());
831 
832     assert(config.fulltypes >= CV4);
833 
834     uint leaf = config.fulltypes == CV8 ? LF_STRUCTURE_V3 : LF_STRUCTURE;
835     uint numidx = leaf == LF_STRUCTURE ? 12 : 18;
836     uint structsize = cast(uint)(closstru.Sstruct.Sstructsize);
837     const char* closname = closstru.Sident.ptr;
838 
839     const len1 = numidx + cv4_numericbytes(structsize);
840     debtyp_t *d = debtyp_alloc(len1 + cv_stringbytes(closname));
841     cv4_storenumeric(d.data.ptr + numidx, structsize);
842     cv_namestring(d.data.ptr + len1, closname);
843 
844     if (leaf == LF_STRUCTURE)
845     {
846         TOWORD(d.data.ptr + 8,0);          // dList
847         TOWORD(d.data.ptr + 10,0);         // vshape is 0 (no virtual functions)
848     }
849     else // LF_STRUCTURE_V3
850     {
851         TOLONG(d.data.ptr + 10,0);         // dList
852         TOLONG(d.data.ptr + 14,0);         // vshape is 0 (no virtual functions)
853     }
854     TOWORD(d.data.ptr,leaf);
855 
856     // Assign a number to prevent infinite recursion if a struct member
857     // references the same struct.
858     const length_save = d.length;
859     d.length = 0;                      // so cv_debtyp() will allocate new
860     const idx_t typidx = cv_debtyp(d);
861     d.length = length_save;            // restore length
862 
863     // Compute the number of fields (nfields), and the length of the fieldlist record (flistlen)
864     uint nfields = 0;
865     uint flistlen = 2;
866     for (auto sl = closstru.Sstruct.Sfldlst; sl; sl = list_next(sl))
867     {
868         Symbol *sf = list_symbol(sl);
869         uint thislen = (config.fulltypes == CV8 ? 8 : 6);
870         thislen += cv4_signednumericbytes(cast(uint)sf.Smemoff);
871         thislen += cv_stringbytes(sf.Sident.ptr);
872         thislen = cv_align(null, thislen);
873 
874         if (config.fulltypes != CV8 && flistlen + thislen > CV4_NAMELENMAX)
875             break; // Too long, fail gracefully
876 
877         flistlen += thislen;
878         nfields++;
879     }
880 
881     // Generate fieldlist type record
882     debtyp_t *dt = debtyp_alloc(flistlen);
883     ubyte *p = dt.data.ptr;
884 
885     // And fill it in
886     TOWORD(p, config.fulltypes == CV8 ? LF_FIELDLIST_V2 : LF_FIELDLIST);
887     uint flistoff = 2;
888     for (auto sl = closstru.Sstruct.Sfldlst; sl && flistoff < flistlen; sl = list_next(sl))
889     {
890         Symbol *sf = list_symbol(sl);
891         idx_t vtypidx = cv_typidx(sf.Stype);
892         flistoff += writeField(p + flistoff, sf.Sident.ptr, 3 /*public*/, vtypidx, cast(uint)sf.Smemoff);
893     }
894 
895     //dbg_printf("fnamelen = %d, p-dt.data.ptr = %d\n",fnamelen,p-dt.data.ptr);
896     assert(flistoff == flistlen);
897     const idx_t fieldlist = cv_debtyp(dt);
898 
899     uint property = 0;
900     TOWORD(d.data.ptr + 2, nfields);
901     if (config.fulltypes == CV8)
902     {
903         TOWORD(d.data.ptr + 4,property);
904         TOLONG(d.data.ptr + 6,fieldlist);
905     }
906     else
907     {
908         TOWORD(d.data.ptr + 4,fieldlist);
909         TOWORD(d.data.ptr + 6,property);
910     }
911 
912     cv_udt(closname, typidx);
913 }
914 
915 /* ===================================================================== */
916 
917 /*****************************************
918  * Insert CV info into *p.
919  * Returns:
920  *      number of bytes written, or that would be written if p==null
921  */
922 
923 int cvMember(Dsymbol s, ubyte *p)
924 {
925     scope v = new CVMember(p);
926     s.accept(v);
927     return v.result;
928 }
929 private extern (C++) class CVMember : Visitor
930 {
931     ubyte *p;
932     int result;
933 
934     this(ubyte *p) @safe
935     {
936         this.p = p;
937         result = 0;
938     }
939 
940     alias visit = Visitor.visit;
941 
942     override void visit(Dsymbol s)
943     {
944     }
945 
946     void cvMemberCommon(Dsymbol s, const(char)* id, idx_t typidx)
947     {
948         if (!p)
949             result = cv_stringbytes(id);
950 
951         switch (config.fulltypes)
952         {
953             case CV8:
954                 if (!p)
955                 {
956                     result += 8;
957                     result = cv_align(null, result);
958                 }
959                 else
960                 {
961                     TOWORD(p,LF_NESTTYPE_V3);
962                     TOWORD(p + 2,0);
963                     TOLONG(p + 4,typidx);
964                     result = 8 + cv_namestring(p + 8, id);
965                     result = cv_align(p + result, result);
966                 }
967                 break;
968 
969             case CV4:
970                 if (!p)
971                 {
972                     result += 4;
973                 }
974                 else
975                 {
976                     TOWORD(p,LF_NESTTYPE);
977                     TOWORD(p + 2,typidx);
978                     result = 4 + cv_namestring(p + 4, id);
979                 }
980                 break;
981 
982             default:
983                 assert(0);
984         }
985         debug
986         {
987             if (p)
988             {
989                 int save = result;
990                 p = null;
991                 cvMemberCommon(s, id, typidx);
992                 assert(result == save);
993             }
994         }
995     }
996 
997     override void visit(EnumDeclaration ed)
998     {
999         //printf("EnumDeclaration.cvMember() '%s'\n", d.toChars());
1000 
1001         cvMemberCommon(ed, ed.toChars(), cv4_Denum(ed));
1002     }
1003 
1004     override void visit(FuncDeclaration fd)
1005     {
1006         //printf("FuncDeclaration.cvMember() '%s'\n", fd.toChars());
1007 
1008         if (!fd.type)               // if not compiled in,
1009             return;                 // skip it
1010         if (!fd.type.nextOf())      // if not fully analyzed (e.g. auto return type)
1011             return;                 // skip it
1012 
1013         const id = fd.toChars();
1014 
1015         if (!p)
1016         {
1017             result = 2 + 2 + cgcv.sz_idx + cv_stringbytes(id);
1018             result = cv_align(null, result);
1019             return;
1020         }
1021         else
1022         {
1023             int count = 0;
1024             int mlen = 2;
1025             {
1026                 if (fd.isIntroducing())
1027                     mlen += 4;
1028                 mlen += cgcv.sz_idx * 2;
1029                 count++;
1030             }
1031 
1032             // Allocate and fill it in
1033             debtyp_t *d = debtyp_alloc(mlen);
1034             ubyte *q = d.data.ptr;
1035             TOWORD(q,config.fulltypes == CV8 ? LF_METHODLIST_V2 : LF_METHODLIST);
1036             q += 2;
1037     //      for (s = sf; s; s = s.Sfunc.Foversym)
1038             {
1039                 uint attribute = visibilityToCVAttr(fd.visible().kind);
1040 
1041                 /* 0*4 vanilla method
1042                  * 1*4 virtual method
1043                  * 2*4 static method
1044                  * 3*4 friend method
1045                  * 4*4 introducing virtual method
1046                  * 5*4 pure virtual method
1047                  * 6*4 pure introducing virtual method
1048                  * 7*4 reserved
1049                  */
1050 
1051                 if (fd.isStatic())
1052                     attribute |= 2*4;
1053                 else if (fd.isVirtual())
1054                 {
1055                     if (fd.isIntroducing())
1056                     {
1057                         if (fd.isAbstract())
1058                             attribute |= 6*4;
1059                         else
1060                             attribute |= 4*4;
1061                     }
1062                     else
1063                     {
1064                         if (fd.isAbstract())
1065                             attribute |= 5*4;
1066                         else
1067                             attribute |= 1*4;
1068                     }
1069                 }
1070                 else
1071                     attribute |= 0*4;
1072 
1073                 TOIDX(q,attribute);
1074                 q += cgcv.sz_idx;
1075                 TOIDX(q, cv4_memfunctypidx(fd));
1076                 q += cgcv.sz_idx;
1077                 if (fd.isIntroducing())
1078                 {
1079                     TOLONG(q, fd.vtblIndex * target.ptrsize);
1080                     q += 4;
1081                 }
1082             }
1083             assert(q - d.data.ptr == mlen);
1084 
1085             idx_t typidx = cv_debtyp(d);
1086             if (typidx)
1087             {
1088                 switch (config.fulltypes)
1089                 {
1090                     case CV8:
1091                         TOWORD(p,LF_METHOD_V3);
1092                         goto Lmethod;
1093                     case CV4:
1094                         TOWORD(p,LF_METHOD);
1095                     Lmethod:
1096                         TOWORD(p + 2,count);
1097                         result = 4;
1098                         TOIDX(p + result, typidx);
1099                         result += cgcv.sz_idx;
1100                         result += cv_namestring(p + result, id);
1101                         break;
1102 
1103                     default:
1104                         assert(0);
1105                 }
1106             }
1107             result = cv_align(p + result, result);
1108             debug
1109             {
1110                 int save = result;
1111                 result = 0;
1112                 p = null;
1113                 visit(fd);
1114                 assert(result == save);
1115             }
1116         }
1117     }
1118 
1119     override void visit(VarDeclaration vd)
1120     {
1121         //printf("VarDeclaration.cvMember(p = %p) '%s'\n", p, vd.toChars());
1122 
1123         if (vd.type.toBasetype().ty == Ttuple)
1124             return;
1125 
1126         const id = vd.toChars();
1127 
1128         if (!p)
1129         {
1130             if (vd.isField())
1131             {
1132                 if (config.fulltypes == CV8)
1133                     result += 2;
1134                 result += 6 + cv_stringbytes(id);
1135                 result += cv4_numericbytes(vd.offset);
1136             }
1137             else if (vd.isStatic())
1138             {
1139                 if (config.fulltypes == CV8)
1140                     result += 2;
1141                 result += 6 + cv_stringbytes(id);
1142             }
1143             result = cv_align(null, result);
1144         }
1145         else
1146         {
1147             idx_t typidx = cv_typidx(Type_toCtype(vd.type));
1148             uint attribute = visibilityToCVAttr(vd.visible().kind);
1149             assert((attribute & ~3) == 0);
1150             switch (config.fulltypes)
1151             {
1152                 case CV8:
1153                     if (vd.isField())
1154                     {
1155                         TOWORD(p,LF_MEMBER_V3);
1156                         TOWORD(p + 2,attribute);
1157                         TOLONG(p + 4,typidx);
1158                         cv4_storenumeric(p + 8, vd.offset);
1159                         result = 8 + cv4_numericbytes(vd.offset);
1160                         result += cv_namestring(p + result, id);
1161                     }
1162                     else if (vd.isStatic())
1163                     {
1164                         TOWORD(p,LF_STMEMBER_V3);
1165                         TOWORD(p + 2,attribute);
1166                         TOLONG(p + 4,typidx);
1167                         result = 8;
1168                         result += cv_namestring(p + result, id);
1169                     }
1170                     break;
1171 
1172                 case CV4:
1173                     if (vd.isField())
1174                     {
1175                         TOWORD(p,LF_MEMBER);
1176                         TOWORD(p + 2,typidx);
1177                         TOWORD(p + 4,attribute);
1178                         cv4_storenumeric(p + 6, vd.offset);
1179                         result = 6 + cv4_numericbytes(vd.offset);
1180                         result += cv_namestring(p + result, id);
1181                     }
1182                     else if (vd.isStatic())
1183                     {
1184                         TOWORD(p,LF_STMEMBER);
1185                         TOWORD(p + 2,typidx);
1186                         TOWORD(p + 4,attribute);
1187                         result = 6;
1188                         result += cv_namestring(p + result, id);
1189                     }
1190                     break;
1191 
1192                  default:
1193                     assert(0);
1194             }
1195 
1196             result = cv_align(p + result, result);
1197             debug
1198             {
1199                 int save = result;
1200                 result = 0;
1201                 p = null;
1202                 visit(vd);
1203                 assert(result == save);
1204             }
1205         }
1206     }
1207 }