// Copyright (c) 2004-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com #include #include #include #include #include "mars.h" #include "module.h" #include "mtype.h" #include "declaration.h" #include "statement.h" #include "enum.h" #include "aggregate.h" #include "init.h" #include "attrib.h" #include "id.h" #include "import.h" #include "template.h" #include "rmem.h" #include "cc.h" #include "global.h" #include "oper.h" #include "code.h" #include "type.h" #include "dt.h" #include "cv4.h" #include "cgcv.h" #include "outbuf.h" #include "irstate.h" /* The CV4 debug format is defined in: * "CV4 Symbolic Debug Information Specification" * rev 3.1 March 5, 1993 * Languages Business Unit * Microsoft */ /****************************** * CV4 pg. 25 * Convert D protection attribute to cv attribute. */ unsigned PROTtoATTR(enum PROT prot) { unsigned attribute; switch (prot) { case PROTprivate: attribute = 1; break; case PROTpackage: attribute = 2; break; case PROTprotected: attribute = 2; break; case PROTpublic: attribute = 3; break; case PROTexport: attribute = 3; break; case PROTundefined: case PROTnone: default: //printf("prot = %d\n", prot); assert(0); } return attribute; } unsigned cv4_memfunctypidx(FuncDeclaration *fd) { type *t; debtyp_t *d; unsigned char *p; AggregateDeclaration *ad; //printf("cv4_memfunctypidx(fd = '%s')\n", fd->toChars()); t = fd->type->toCtype(); ad = fd->isMember2(); if (ad) { unsigned nparam; idx_t paramidx; idx_t thisidx; unsigned char call; // It's a member function, which gets a special type record if (fd->isStatic()) thisidx = dttab4[TYvoid]; else { assert(ad->handle); thisidx = cv4_typidx(ad->handle->toCtype()); } paramidx = cv4_arglist(t,&nparam); call = cv4_callconv(t); d = debtyp_alloc(18); p = d->data; TOWORD(p,LF_MFUNCTION); TOWORD(p + 2,cv4_typidx(t->Tnext)); TOWORD(p + 4,cv4_typidx(ad->type->toCtype())); TOWORD(p + 6,thisidx); p[8] = call; p[9] = 0; // reserved TOWORD(p + 10,nparam); TOWORD(p + 12,paramidx); TOLONG(p + 14,0); // thisadjust return cv_debtyp(d); } return cv4_typidx(t); } unsigned cv4_Denum(EnumDeclaration *e) { debtyp_t *d,*dt; unsigned nfields,fnamelen; unsigned len; unsigned property; unsigned attribute; const char *id; idx_t typidx; //dbg_printf("cv4_Denum(%s)\n", e->toChars()); property = 0; if (!e->members || !e->memtype) property |= 0x80; // enum is forward referenced id = e->toPrettyChars(); len = 10; d = debtyp_alloc(len + cv_stringbytes(id)); TOWORD(d->data,LF_ENUM); TOWORD(d->data + 4,e->memtype ? cv4_typidx(e->memtype->toCtype()) : 0); TOWORD(d->data + 8,property); len += cv_namestring(d->data + len,id); d->length = 0; // so cv_debtyp() will allocate new typidx = cv_debtyp(d); d->length = len; // restore length // Compute the number of fields, and the length of the fieldlist record nfields = 0; fnamelen = 2; if (!property) { for (size_t i = 0; i < e->members->dim; i++) { EnumMember *sf = (e->members->tdata()[i])->isEnumMember(); dinteger_t value; if (sf) { value = sf->value->toInteger(); unsigned fnamelen1 = fnamelen; // store only member's simple name fnamelen += 4 + cv4_numericbytes(value) + cv_stringbytes(sf->toChars()); /* Optlink dies on longer ones, so just truncate */ if (fnamelen > 0xB000) // 0xB000 found by trial and error { fnamelen = fnamelen1; // back up break; // and skip the rest } nfields++; } } } TOWORD(d->data + 2,nfields); // If forward reference, then field list is 0 if (property) { TOWORD(d->data + 6,0); return typidx; } // Generate fieldlist type record dt = debtyp_alloc(fnamelen); TOWORD(dt->data,LF_FIELDLIST); // And fill it in unsigned j = 2; unsigned fieldi = 0; for (size_t i = 0; i < e->members->dim; i++) { EnumMember *sf = (e->members->tdata()[i])->isEnumMember(); dinteger_t value; if (sf) { fieldi++; if (fieldi > nfields) break; // chop off the rest value = sf->value->toInteger(); TOWORD(dt->data + j,LF_ENUMERATE); attribute = 0; TOWORD(dt->data + j + 2,attribute); cv4_storenumeric(dt->data + j + 4,value); j += 4 + cv4_numericbytes(value); // store only member's simple name j += cv_namestring(dt->data + j, sf->toChars()); // If enum is not a member of a class, output enum members as constants // if (!isclassmember(s)) // { // cv4_outsym(sf); // } } } assert(j == fnamelen); TOWORD(d->data + 6,cv_debtyp(dt)); // cv4_outsym(s); return typidx; } /* ==================================================================== */ /**************************** * Emit symbolic debug info in CV format. */ void TypedefDeclaration::toDebug() { //printf("TypedefDeclaration::toDebug('%s')\n", toChars()); assert(config.fulltypes >= CV4); // If it is a member, it is handled by cvMember() if (!isMember()) { if (basetype->ty == Ttuple) return; unsigned length; const char *id = toPrettyChars(); idx_t typidx = cv4_typidx(basetype->toCtype()); unsigned len = strlen(id); unsigned char *debsym = (unsigned char *) alloca(39 + IDOHD + len); // Output a 'user-defined type' for the tag name TOWORD(debsym + 2,S_UDT); TOIDX(debsym + 4,typidx); length = 2 + 2 + cgcv.sz_idx; length += cv_namestring(debsym + length,id); TOWORD(debsym,length - 2); assert(length <= 40 + len); obj_write_bytes(SegData[DEBSYM],length,debsym); } } void EnumDeclaration::toDebug() { //printf("EnumDeclaration::toDebug('%s')\n", toChars()); assert(config.fulltypes >= CV4); // If it is a member, it is handled by cvMember() if (!isMember()) { unsigned length; const char *id = toPrettyChars(); idx_t typidx = cv4_Denum(this); unsigned len = strlen(id); unsigned char *debsym = (unsigned char *) alloca(39 + IDOHD + len); // Output a 'user-defined type' for the tag name TOWORD(debsym + 2,S_UDT); TOIDX(debsym + 4,typidx); length = 2 + 2 + cgcv.sz_idx; length += cv_namestring(debsym + length,id); TOWORD(debsym,length - 2); assert(length <= 40 + len); obj_write_bytes(SegData[DEBSYM],length,debsym); } } // Closure variables for Lambda cv_mem_count struct CvMemberCount { unsigned nfields; unsigned fnamelen; }; // Lambda function int cv_mem_count(Dsymbol *s, void *param) { CvMemberCount *pmc = (CvMemberCount *)param; int nwritten = s->cvMember(NULL); if (nwritten) { pmc->fnamelen += nwritten; pmc->nfields++; } return 0; } // Lambda function int cv_mem_p(Dsymbol *s, void *param) { unsigned char **pp = (unsigned char **)param; *pp += s->cvMember(*pp); return 0; } void StructDeclaration::toDebug() { unsigned leaf; unsigned property; unsigned nfields; unsigned fnamelen; const char *id; targ_size_t size; unsigned numidx; debtyp_t *d,*dt; unsigned len; int count; // COUNT field in LF_CLASS unsigned char *p; idx_t typidx = 0; //printf("StructDeclaration::toDebug('%s')\n", toChars()); assert(config.fulltypes >= CV4); if (isAnonymous()) return /*0*/; if (typidx) // if reference already generated return /*typidx*/; // use already existing reference property = 0; if (!members) { size = 0; property |= 0x80; // forward reference } else size = structsize; if (parent->isAggregateDeclaration()) // if class is nested property |= 8; // if (st->Sctor || st->Sdtor) // property |= 2; // class has ctors and/or dtors // if (st->Sopoverload) // property |= 4; // class has overloaded operators // if (st->Scastoverload) // property |= 0x40; // class has casting methods // if (st->Sopeq && !(st->Sopeq->Sfunc->Fflags & Fnodebug)) // property |= 0x20; // class has overloaded assignment id = toPrettyChars(); numidx = isUnionDeclaration() ? 8 : 12; len = numidx + cv4_numericbytes(size); d = debtyp_alloc(len + cv_stringbytes(id)); cv4_storenumeric(d->data + numidx,size); len += cv_namestring(d->data + len,id); leaf = isUnionDeclaration() ? LF_UNION : LF_STRUCTURE; if (!isUnionDeclaration()) { TOWORD(d->data + 8,0); // dList TOWORD(d->data + 10,0); // vshape is 0 (no virtual functions) } TOWORD(d->data,leaf); // Assign a number to prevent infinite recursion if a struct member // references the same struct. d->length = 0; // so cv_debtyp() will allocate new typidx = cv_debtyp(d); d->length = len; // restore length if (!members) // if reference only { TOWORD(d->data + 2,0); // count: number of fields is 0 TOWORD(d->data + 4,0); // field list is 0 TOWORD(d->data + 6,property); return /*typidx*/; } // Compute the number of fields (nfields), and the length of the fieldlist record (fnamelen) CvMemberCount mc; mc.nfields = 0; mc.fnamelen = 2; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->apply(&cv_mem_count, &mc); } nfields = mc.nfields; fnamelen = mc.fnamelen; count = nfields; TOWORD(d->data + 2,count); TOWORD(d->data + 6,property); // Generate fieldlist type record dt = debtyp_alloc(fnamelen); p = dt->data; // And fill it in TOWORD(p,LF_FIELDLIST); p += 2; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->apply(&cv_mem_p, &p); } //dbg_printf("fnamelen = %d, p-dt->data = %d\n",fnamelen,p-dt->data); assert(p - dt->data == fnamelen); TOWORD(d->data + 4,cv_debtyp(dt)); // cv4_outsym(s); unsigned char *debsym; unsigned length; len = strlen(id); debsym = (unsigned char *) alloca(39 + IDOHD + len); // Output a 'user-defined type' for the tag name TOWORD(debsym + 2,S_UDT); TOIDX(debsym + 4,typidx); length = 2 + 2 + cgcv.sz_idx; length += cv_namestring(debsym + length,id); TOWORD(debsym,length - 2); assert(length <= 40 + len); obj_write_bytes(SegData[DEBSYM],length,debsym); // return typidx; } void ClassDeclaration::toDebug() { unsigned leaf; unsigned property; unsigned nfields; unsigned fnamelen; const char *id; targ_size_t size; unsigned numidx; debtyp_t *d,*dt; unsigned len; int i; int count; // COUNT field in LF_CLASS unsigned char *p; idx_t typidx = 0; //printf("ClassDeclaration::toDebug('%s')\n", toChars()); assert(config.fulltypes >= CV4); if (isAnonymous()) return /*0*/; if (typidx) // if reference already generated return /*typidx*/; // use already existing reference property = 0; if (!members) { size = 0; property |= 0x80; // forward reference } else size = structsize; if (parent->isAggregateDeclaration()) // if class is nested property |= 8; if (ctor || dtors.dim) property |= 2; // class has ctors and/or dtors // if (st->Sopoverload) // property |= 4; // class has overloaded operators // if (st->Scastoverload) // property |= 0x40; // class has casting methods // if (st->Sopeq && !(st->Sopeq->Sfunc->Fflags & Fnodebug)) // property |= 0x20; // class has overloaded assignment id = isCPPinterface() ? ident->toChars() : toPrettyChars(); numidx = isUnionDeclaration() ? 8 : 12; len = numidx + cv4_numericbytes(size); d = debtyp_alloc(len + cv_stringbytes(id)); cv4_storenumeric(d->data + numidx,size); len += cv_namestring(d->data + len,id); leaf = LF_CLASS; TOWORD(d->data + 8,0); // dList if (1) { debtyp_t *vshape; unsigned char descriptor; size_t n = vtbl.dim; // number of virtual functions if (n == 0) { TOWORD(d->data + 10,0); // vshape is 0 } else { vshape = debtyp_alloc(4 + (n + 1) / 2); TOWORD(vshape->data,LF_VTSHAPE); TOWORD(vshape->data + 2,1); n = 0; descriptor = 0; for (size_t i = 0; i < vtbl.dim; i++) { FuncDeclaration *fd = (FuncDeclaration *)vtbl.tdata()[i]; //if (intsize == 4) descriptor |= 5; vshape->data[4 + n / 2] = descriptor; descriptor <<= 4; n++; } TOWORD(d->data + 10,cv_debtyp(vshape)); // vshape } } else TOWORD(d->data + 10,0); // vshape is 0 (no virtual functions) TOWORD(d->data,leaf); // Assign a number to prevent infinite recursion if a struct member // references the same struct. d->length = 0; // so cv_debtyp() will allocate new typidx = cv_debtyp(d); d->length = len; // restore length if (!members) // if reference only { TOWORD(d->data + 2,0); // count: number of fields is 0 TOWORD(d->data + 4,0); // field list is 0 TOWORD(d->data + 6,property); return /*typidx*/; } // Compute the number of fields (nfields), and the length of the fieldlist record (fnamelen) CvMemberCount mc; mc.nfields = 0; mc.fnamelen = 2; // Add in base classes for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *bc = (*baseclasses)[i]; mc.nfields++; mc.fnamelen += 6 + cv4_numericbytes(bc->offset); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->apply(&cv_mem_count, &mc); } nfields = mc.nfields; fnamelen = mc.fnamelen; count = nfields; TOWORD(d->data + 2,count); TOWORD(d->data + 6,property); // Generate fieldlist type record dt = debtyp_alloc(fnamelen); p = dt->data; // And fill it in TOWORD(p,LF_FIELDLIST); p += 2; // Add in base classes for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *bc = (*baseclasses)[i]; idx_t typidx; unsigned attribute; typidx = cv4_typidx(bc->base->type->toCtype()->Tnext); attribute = PROTtoATTR(bc->protection); TOWORD(p,LF_BCLASS); TOWORD(p + 2,typidx); TOWORD(p + 4,attribute); p += 6; cv4_storenumeric(p, bc->offset); p += cv4_numericbytes(bc->offset); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->apply(&cv_mem_p, &p); } //dbg_printf("fnamelen = %d, p-dt->data = %d\n",fnamelen,p-dt->data); assert(p - dt->data == fnamelen); TOWORD(d->data + 4,cv_debtyp(dt)); // cv4_outsym(s); unsigned char *debsym; unsigned length; len = strlen(id); debsym = (unsigned char *) alloca(39 + IDOHD + len); // Output a 'user-defined type' for the tag name TOWORD(debsym + 2,S_UDT); TOIDX(debsym + 4,typidx); length = 2 + 2 + cgcv.sz_idx; length += cv_namestring(debsym + length,id); TOWORD(debsym,length - 2); assert(length <= 40 + len); obj_write_bytes(SegData[DEBSYM],length,debsym); // return typidx; } /* ===================================================================== */ /***************************************** * Insert CV info into *p. * Returns: * number of bytes written, or that would be written if p==NULL */ int Dsymbol::cvMember(unsigned char *p) { return 0; } int TypedefDeclaration::cvMember(unsigned char *p) { char *id; idx_t typidx; int nwritten = 0; //printf("TypedefDeclaration::cvMember() '%s'\n", toChars()); id = toChars(); if (!p) { nwritten = 4 + cv_stringbytes(id); } else { TOWORD(p,LF_NESTTYPE); typidx = cv4_typidx(basetype->toCtype()); TOWORD(p + 2,typidx); nwritten = 4 + cv_namestring(p + 4, id); } return nwritten; } int EnumDeclaration::cvMember(unsigned char *p) { char *id; idx_t typidx; int nwritten = 0; //printf("EnumDeclaration::cvMember() '%s'\n", toChars()); id = toChars(); if (!p) { nwritten = 4 + cv_stringbytes(id); } else { TOWORD(p,LF_NESTTYPE); typidx = cv4_Denum(this); TOWORD(p + 2,typidx); nwritten = 4 + cv_namestring(p + 4, id); } return nwritten; } int FuncDeclaration::cvMember(unsigned char *p) { char *id; idx_t typidx; unsigned attribute; int nwritten = 0; debtyp_t *d; //printf("FuncDeclaration::cvMember() '%s'\n", toChars()); if (!type) // if not compiled in, return 0; // skip it id = toChars(); if (!p) { nwritten = 6 + cv_stringbytes(id); } else { int count; int mlen; unsigned char *q; count = 0; mlen = 2; { if (introducing) mlen += 4; mlen += cgcv.sz_idx * 2; count++; } // Allocate and fill it in d = debtyp_alloc(mlen); q = d->data; TOWORD(q,LF_METHODLIST); q += 2; // for (s = sf; s; s = s->Sfunc->Foversym) { attribute = PROTtoATTR(prot()); /* 0*4 vanilla method * 1*4 virtual method * 2*4 static method * 3*4 friend method * 4*4 introducing virtual method * 5*4 pure virtual method * 6*4 pure introducing virtual method * 7*4 reserved */ if (isStatic()) attribute |= 2*4; else if (isVirtual()) { if (introducing) { if (isAbstract()) attribute |= 6*4; else attribute |= 4*4; } else { if (isAbstract()) attribute |= 5*4; else attribute |= 1*4; } } else attribute |= 0*4; TOIDX(q,attribute); q += cgcv.sz_idx; TOIDX(q, cv4_memfunctypidx(this)); q += cgcv.sz_idx; if (introducing) { TOLONG(q, vtblIndex * PTRSIZE); q += 4; } } assert(q - d->data == mlen); typidx = cv_debtyp(d); if (typidx) { TOWORD(p,LF_METHOD); TOWORD(p + 2,count); nwritten = 4; TOIDX(p + nwritten, typidx); nwritten += cgcv.sz_idx; nwritten += cv_namestring(p + nwritten, id); } } return nwritten; } int VarDeclaration::cvMember(unsigned char *p) { char *id; idx_t typidx; unsigned attribute; int nwritten = 0; //printf("VarDeclaration::cvMember(p = %p) '%s'\n", p, toChars()); if (type->toBasetype()->ty == Ttuple) return 0; id = toChars(); if (!p) { if (storage_class & STCfield) { nwritten += 6 + cv4_numericbytes(offset) + cv_stringbytes(id); } else if (isStatic()) { nwritten += 6 + cv_stringbytes(id); } } else if (storage_class & STCfield) { TOWORD(p,LF_MEMBER); typidx = cv_typidx(type->toCtype()); attribute = PROTtoATTR(prot()); assert((attribute & ~3) == 0); TOWORD(p + 2,typidx); TOWORD(p + 4,attribute); cv4_storenumeric(p + 6, offset); nwritten = 6 + cv4_numericbytes( offset); nwritten += cv_namestring(p + nwritten, id); } else if (isStatic()) { TOWORD(p,LF_STMEMBER); typidx = cv_typidx(type->toCtype()); attribute = PROTtoATTR(prot()); assert((attribute & ~3) == 0); TOWORD(p + 2,typidx); TOWORD(p + 4,attribute); nwritten = 6 + cv_namestring(p + 6, id); } return nwritten; }