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