/** * Compiler implementation of the $(LINK2 http://www.dlang.org, D programming language) * * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved * Authors: Walter Bright, http://www.digitalmars.com * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(DMDSRC _dmangle.d) */ module ddmd.dmangle; import core.stdc.ctype; import core.stdc.stdio; import core.stdc.string; import ddmd.aggregate; import ddmd.arraytypes; import ddmd.cppmangle; import ddmd.dclass; import ddmd.declaration; import ddmd.dmodule; import ddmd.dsymbol; import ddmd.dtemplate; import ddmd.expression; import ddmd.func; import ddmd.globals; import ddmd.id; import ddmd.mtype; import ddmd.root.ctfloat; import ddmd.root.outbuffer; import ddmd.utf; import ddmd.visitor; private immutable char[TMAX] mangleChar = [ Tchar : 'a', Tbool : 'b', Tcomplex80 : 'c', Tfloat64 : 'd', Tfloat80 : 'e', Tfloat32 : 'f', Tint8 : 'g', Tuns8 : 'h', Tint32 : 'i', Timaginary80 : 'j', Tuns32 : 'k', Tint64 : 'l', Tuns64 : 'm', Tnone : 'n', Tnull : 'n', // yes, same as TypeNone Timaginary32 : 'o', Timaginary64 : 'p', Tcomplex32 : 'q', Tcomplex64 : 'r', Tint16 : 's', Tuns16 : 't', Twchar : 'u', Tvoid : 'v', Tdchar : 'w', // x // const // y // immutable Tint128 : 'z', // zi Tuns128 : 'z', // zk Tarray : 'A', Ttuple : 'B', Tclass : 'C', Tdelegate : 'D', Tenum : 'E', Tfunction : 'F', // D function Tsarray : 'G', Taarray : 'H', Tident : 'I', // J // out // K // ref // L // lazy // M // has this, or scope // N // Nh:vector Ng:wild // O // shared Tpointer : 'P', // Q Treference : 'R', Tstruct : 'S', // T // Ttypedef // U // C function // V // Pascal function // W // Windows function // X // variadic T t...) // Y // variadic T t,...) // Z // not variadic, end of parameters // '@' shouldn't appear anywhere in the deco'd names Tinstance : '@', Terror : '@', Ttypeof : '@', Tslice : '@', Treturn : '@', Tvector : '@', ]; unittest { foreach (i, mangle; mangleChar) { if (mangle == char.init) { fprintf(stderr, "ty = %u\n", cast(uint)i); assert(0); } } } /*********************** * Mangle basic type ty to buf. */ private void tyToDecoBuffer(OutBuffer* buf, int ty) { const c = mangleChar[ty]; buf.writeByte(c); if (c == 'z') buf.writeByte(ty == Tint128 ? 'i' : 'k'); } /********************************* * Mangling for mod. */ private void MODtoDecoBuffer(OutBuffer* buf, MOD mod) { switch (mod) { case 0: break; case MODconst: buf.writeByte('x'); break; case MODimmutable: buf.writeByte('y'); break; case MODshared: buf.writeByte('O'); break; case MODshared | MODconst: buf.writestring("Ox"); break; case MODwild: buf.writestring("Ng"); break; case MODwildconst: buf.writestring("Ngx"); break; case MODshared | MODwild: buf.writestring("ONg"); break; case MODshared | MODwildconst: buf.writestring("ONgx"); break; default: assert(0); } } extern (C++) final class Mangler : Visitor { alias visit = super.visit; public: OutBuffer* buf; extern (D) this(OutBuffer* buf) { this.buf = buf; } //////////////////////////////////////////////////////////////////////////// /************************************************** * Type mangling */ void visitWithMask(Type t, ubyte modMask) { if (t.deco && modMask == 0) { buf.writestring(t.deco); // don't need to recreate it return; } if (modMask != t.mod) { MODtoDecoBuffer(buf, t.mod); } t.accept(this); } override void visit(Type t) { tyToDecoBuffer(buf, t.ty); } override void visit(TypeNext t) { visit(cast(Type)t); visitWithMask(t.next, t.mod); } override void visit(TypeVector t) { buf.writestring("Nh"); visitWithMask(t.basetype, t.mod); } override void visit(TypeSArray t) { visit(cast(Type)t); if (t.dim) buf.printf("%u", cast(uint)t.dim.toInteger()); if (t.next) visitWithMask(t.next, t.mod); } override void visit(TypeDArray t) { visit(cast(Type)t); if (t.next) visitWithMask(t.next, t.mod); } override void visit(TypeAArray t) { visit(cast(Type)t); visitWithMask(t.index, 0); visitWithMask(t.next, t.mod); } override void visit(TypeFunction t) { //printf("TypeFunction.toDecoBuffer() t = %p %s\n", t, t.toChars()); //static int nest; if (++nest == 50) *(char*)0=0; mangleFuncType(t, t, t.mod, t.next); } void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret) { //printf("mangleFuncType() %s\n", t.toChars()); if (t.inuse) { t.inuse = 2; // flag error to caller return; } t.inuse++; if (modMask != t.mod) MODtoDecoBuffer(buf, t.mod); ubyte mc; switch (t.linkage) { case LINKd: mc = 'F'; break; case LINKc: mc = 'U'; break; case LINKwindows: mc = 'W'; break; case LINKpascal: mc = 'V'; break; case LINKcpp: mc = 'R'; break; case LINKobjc: mc = 'Y'; break; default: assert(0); } buf.writeByte(mc); if (ta.purity || ta.isnothrow || ta.isnogc || ta.isproperty || ta.isref || ta.trust || ta.isreturn || ta.isscope) { if (ta.purity) buf.writestring("Na"); if (ta.isnothrow) buf.writestring("Nb"); if (ta.isref) buf.writestring("Nc"); if (ta.isproperty) buf.writestring("Nd"); if (ta.isnogc) buf.writestring("Ni"); if (ta.isreturn) buf.writestring("Nj"); if (ta.isscope && !ta.isreturn) buf.writestring("M"); switch (ta.trust) { case TRUSTtrusted: buf.writestring("Ne"); break; case TRUSTsafe: buf.writestring("Nf"); break; default: break; } } // Write argument types paramsToDecoBuffer(t.parameters); //if (buf.data[buf.offset - 1] == '@') assert(0); buf.writeByte('Z' - t.varargs); // mark end of arg list if (tret !is null) visitWithMask(tret, 0); t.inuse--; } override void visit(TypeIdentifier t) { visit(cast(Type)t); const(char)* name = t.ident.toChars(); size_t len = strlen(name); buf.printf("%u%s", cast(uint)len, name); } override void visit(TypeEnum t) { visit(cast(Type)t); t.sym.accept(this); } override void visit(TypeStruct t) { //printf("TypeStruct.toDecoBuffer('%s') = '%s'\n", t.toChars(), name); visit(cast(Type)t); t.sym.accept(this); } override void visit(TypeClass t) { //printf("TypeClass.toDecoBuffer('%s' mod=%x) = '%s'\n", t.toChars(), mod, name); visit(cast(Type)t); t.sym.accept(this); } override void visit(TypeTuple t) { //printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars()); visit(cast(Type)t); OutBuffer buf2; buf2.reserve(32); scope Mangler v = new Mangler(&buf2); v.paramsToDecoBuffer(t.arguments); auto s = buf2.peekSlice(); int len = cast(int)s.length; buf.printf("%d%.*s", len, len, s.ptr); } override void visit(TypeNull t) { visit(cast(Type)t); } //////////////////////////////////////////////////////////////////////////// void mangleDecl(Declaration sthis) { mangleParent(sthis); assert(sthis.ident); const(char)* id = sthis.ident.toChars(); toBuffer(id, sthis); if (FuncDeclaration fd = sthis.isFuncDeclaration()) { mangleFunc(fd, false); } else if (sthis.type.deco) { buf.writestring(sthis.type.deco); } else assert(0); } void mangleParent(Dsymbol s) { Dsymbol p; if (TemplateInstance ti = s.isTemplateInstance()) p = ti.isTemplateMixin() ? ti.parent : ti.tempdecl.parent; else p = s.parent; if (p) { mangleParent(p); if (p.getIdent()) { const(char)* id = p.ident.toChars(); toBuffer(id, s); if (FuncDeclaration f = p.isFuncDeclaration()) mangleFunc(f, true); } else buf.writeByte('0'); } } void mangleFunc(FuncDeclaration fd, bool inParent) { //printf("deco = '%s'\n", fd.type.deco ? fd.type.deco : "null"); //printf("fd.type = %s\n", fd.type.toChars()); if (fd.needThis() || fd.isNested()) buf.writeByte('M'); if (inParent) { TypeFunction tf = cast(TypeFunction)fd.type; TypeFunction tfo = cast(TypeFunction)fd.originalType; mangleFuncType(tf, tfo, 0, null); } else if (fd.type.deco) { buf.writestring(fd.type.deco); } else { printf("[%s] %s %s\n", fd.loc.toChars(), fd.toChars(), fd.type.toChars()); assert(0); // don't mangle function until semantic3 done. } } /************************************************************ * Write length prefixed string to buf. */ void toBuffer(const(char)* id, Dsymbol s) { size_t len = strlen(id); if (len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone s.error("excessive length %llu for symbol, possible recursive expansion?", len); else { buf.printf("%u", cast(uint)len); buf.write(id, len); } } override void visit(Declaration d) { //printf("Declaration.mangle(this = %p, '%s', parent = '%s', linkage = %d)\n", // d, d.toChars(), d.parent ? d.parent.toChars() : "null", d.linkage); if (!d.parent || d.parent.isModule() || d.linkage == LINKcpp) // if at global scope { switch (d.linkage) { case LINKd: break; case LINKc: case LINKwindows: case LINKpascal: case LINKobjc: buf.writestring(d.ident.toChars()); return; case LINKcpp: buf.writestring(toCppMangle(d)); return; case LINKdefault: d.error("forward declaration"); buf.writestring(d.ident.toChars()); return; default: fprintf(stderr, "'%s', linkage = %d\n", d.toChars(), d.linkage); assert(0); } } buf.writestring("_D"); mangleDecl(d); debug { const slice = buf.peekSlice(); assert(slice.length); foreach (const char c; slice) { assert(c == '_' || c == '@' || c == '?' || c == '$' || isalnum(c) || c & 0x80); } } } /****************************************************************************** * Normally FuncDeclaration and FuncAliasDeclaration have overloads. * If and only if there is no overloads, mangle() could return * exact mangled name. * * module test; * void foo(long) {} // _D4test3fooFlZv * void foo(string) {} // _D4test3fooFAyaZv * * // from FuncDeclaration.mangle(). * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo" * // by calling Dsymbol.mangle() * * // from FuncAliasDeclaration.mangle() * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv" * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv" * * If a function has no overloads, .mangleof property still returns exact mangled name. * * void bar() {} * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv" * // by calling FuncDeclaration.mangleExact(). */ override void visit(FuncDeclaration fd) { if (fd.isUnique()) mangleExact(fd); else visit(cast(Dsymbol)fd); } // ditto override void visit(FuncAliasDeclaration fd) { FuncDeclaration f = fd.toAliasFunc(); FuncAliasDeclaration fa = f.isFuncAliasDeclaration(); if (!fd.hasOverloads && !fa) { mangleExact(f); return; } if (fa) { fa.accept(this); return; } visit(cast(Dsymbol)fd); } override void visit(OverDeclaration od) { if (od.overnext) { visit(cast(Dsymbol)od); return; } if (FuncDeclaration fd = od.aliassym.isFuncDeclaration()) { if (!od.hasOverloads || fd.isUnique()) { mangleExact(fd); return; } } if (TemplateDeclaration td = od.aliassym.isTemplateDeclaration()) { if (!od.hasOverloads || td.overnext is null) { td.accept(this); return; } } visit(cast(Dsymbol)od); } void mangleExact(FuncDeclaration fd) { assert(!fd.isFuncAliasDeclaration()); if (fd.mangleOverride) { buf.writestring(fd.mangleOverride); return; } if (fd.isMain()) { buf.writestring("_Dmain"); return; } if (fd.isWinMain() || fd.isDllMain() || fd.ident == Id.tls_get_addr) { buf.writestring(fd.ident.toChars()); return; } visit(cast(Declaration)fd); } override void visit(VarDeclaration vd) { if (vd.mangleOverride) { buf.writestring(vd.mangleOverride); return; } visit(cast(Declaration)vd); } override void visit(AggregateDeclaration ad) { ClassDeclaration cd = ad.isClassDeclaration(); Dsymbol parentsave = ad.parent; if (cd) { /* These are reserved to the compiler, so keep simple * names for them. */ if (cd.ident == Id.Exception && cd.parent.ident == Id.object || cd.ident == Id.TypeInfo || cd.ident == Id.TypeInfo_Struct || cd.ident == Id.TypeInfo_Class || cd.ident == Id.TypeInfo_Tuple || cd == ClassDeclaration.object || cd == Type.typeinfoclass || cd == Module.moduleinfo || strncmp(cd.ident.toChars(), "TypeInfo_", 9) == 0) { // Don't mangle parent ad.parent = null; } } visit(cast(Dsymbol)ad); ad.parent = parentsave; } override void visit(TemplateInstance ti) { version (none) { printf("TemplateInstance.mangle() %p %s", ti, ti.toChars()); if (ti.parent) printf(" parent = %s %s", ti.parent.kind(), ti.parent.toChars()); printf("\n"); } if (!ti.tempdecl) ti.error("is not defined"); else mangleParent(ti); ti.getIdent(); const(char)* id = ti.ident ? ti.ident.toChars() : ti.toChars(); toBuffer(id, ti); //printf("TemplateInstance.mangle() %s = %s\n", ti.toChars(), ti.id); } override void visit(Dsymbol s) { version (none) { printf("Dsymbol.mangle() '%s'", s.toChars()); if (s.parent) printf(" parent = %s %s", s.parent.kind(), s.parent.toChars()); printf("\n"); } mangleParent(s); auto id = s.ident ? s.ident.toChars() : s.toChars(); toBuffer(id, s); //printf("Dsymbol.mangle() %s = %s\n", s.toChars(), id); } //////////////////////////////////////////////////////////////////////////// override void visit(Expression e) { e.error("expression %s is not a valid template value argument", e.toChars()); } override void visit(IntegerExp e) { if (cast(sinteger_t)e.value < 0) buf.printf("N%lld", -e.value); else buf.printf("i%lld", e.value); } override void visit(RealExp e) { buf.writeByte('e'); realToMangleBuffer(e.value); } void realToMangleBuffer(real_t value) { /* Rely on %A to get portable mangling. * Must munge result to get only identifier characters. * * Possible values from %A => mangled result * NAN => NAN * -INF => NINF * INF => INF * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79 * 0X1.9P+2 => 19P2 */ if (CTFloat.isNaN(value)) buf.writestring("NAN"); // no -NAN bugs else if (CTFloat.isInfinity(value)) buf.writestring(value < CTFloat.zero ? "NINF" : "INF"); else { enum BUFFER_LEN = 36; char[BUFFER_LEN] buffer; const n = CTFloat.sprint(buffer.ptr, 'A', value); assert(n < BUFFER_LEN); for (int i = 0; i < n; i++) { char c = buffer[i]; switch (c) { case '-': buf.writeByte('N'); break; case '+': case 'X': case '.': break; case '0': if (i < 2) break; // skip leading 0X goto default; default: buf.writeByte(c); break; } } } } override void visit(ComplexExp e) { buf.writeByte('c'); realToMangleBuffer(e.toReal()); buf.writeByte('c'); // separate the two realToMangleBuffer(e.toImaginary()); } override void visit(NullExp e) { buf.writeByte('n'); } override void visit(StringExp e) { char m; OutBuffer tmp; const(char)[] q; /* Write string in UTF-8 format */ switch (e.sz) { case 1: m = 'a'; q = e.string[0 .. e.len]; break; case 2: m = 'w'; for (size_t u = 0; u < e.len;) { dchar c; const p = utf_decodeWchar(e.wstring, e.len, u, c); if (p) e.error("%s", p); else tmp.writeUTF8(c); } q = tmp.peekSlice(); break; case 4: m = 'd'; for (size_t u = 0; u < e.len; u++) { uint c = (cast(uint*)e.string)[u]; if (!utf_isValidDchar(c)) e.error("invalid UCS-32 char \\U%08x", c); else tmp.writeUTF8(c); } q = tmp.peekSlice(); break; default: assert(0); } buf.reserve(1 + 11 + 2 * q.length); buf.writeByte(m); buf.printf("%d_", cast(int)q.length); // nbytes <= 11 size_t qi = 0; for (char* p = cast(char*)buf.data + buf.offset, pend = p + 2 * q.length; p < pend; p += 2, ++qi) { char hi = (q[qi] >> 4) & 0xF; p[0] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + 'a'); char lo = q[qi] & 0xF; p[1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + 'a'); } buf.offset += 2 * q.length; } override void visit(ArrayLiteralExp e) { size_t dim = e.elements ? e.elements.dim : 0; buf.printf("A%u", cast(uint)dim); for (size_t i = 0; i < dim; i++) { e.getElement(i).accept(this); } } override void visit(AssocArrayLiteralExp e) { size_t dim = e.keys.dim; buf.printf("A%u", cast(uint)dim); for (size_t i = 0; i < dim; i++) { (*e.keys)[i].accept(this); (*e.values)[i].accept(this); } } override void visit(StructLiteralExp e) { size_t dim = e.elements ? e.elements.dim : 0; buf.printf("S%u", cast(uint)dim); for (size_t i = 0; i < dim; i++) { Expression ex = (*e.elements)[i]; if (ex) ex.accept(this); else buf.writeByte('v'); // 'v' for void } } //////////////////////////////////////////////////////////////////////////// void paramsToDecoBuffer(Parameters* parameters) { //printf("Parameter.paramsToDecoBuffer()\n"); int paramsToDecoBufferDg(size_t n, Parameter p) { p.accept(this); return 0; } Parameter._foreach(parameters, ¶msToDecoBufferDg); } override void visit(Parameter p) { if (p.storageClass & STCscope) buf.writeByte('M'); // 'return inout ref' is the same as 'inout ref' if ((p.storageClass & (STCreturn | STCwild)) == STCreturn) buf.writestring("Nk"); switch (p.storageClass & (STCin | STCout | STCref | STClazy)) { case 0: case STCin: break; case STCout: buf.writeByte('J'); break; case STCref: buf.writeByte('K'); break; case STClazy: buf.writeByte('L'); break; default: debug { printf("storageClass = x%llx\n", p.storageClass & (STCin | STCout | STCref | STClazy)); } assert(0); } visitWithMask(p.type, 0); } } /****************************************************************************** * Returns exact mangled name of function. */ extern (C++) const(char)* mangleExact(FuncDeclaration fd) { if (!fd.mangleString) { OutBuffer buf; scope Mangler v = new Mangler(&buf); v.mangleExact(fd); fd.mangleString = buf.extractString(); } return fd.mangleString; } extern (C++) void mangleToBuffer(Type t, OutBuffer* buf) { if (t.deco) buf.writestring(t.deco); else { scope Mangler v = new Mangler(buf); v.visitWithMask(t, 0); } } extern (C++) void mangleToBuffer(Expression e, OutBuffer* buf) { scope Mangler v = new Mangler(buf); e.accept(v); } extern (C++) void mangleToBuffer(Dsymbol s, OutBuffer* buf) { scope Mangler v = new Mangler(buf); s.accept(v); }