| /** |
| * Does name mangling for `extern(D)` symbols. |
| * |
| * Specification: $(LINK2 https://dlang.org/spec/abi.html#name_mangling, Name Mangling) |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: Walter Bright, https://www.digitalmars.com |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d, _dmangle.d) |
| * Documentation: https://dlang.org/phobos/dmd_dmangle.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmangle.d |
| * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/ |
| */ |
| |
| module dmd.dmangle; |
| |
| import dmd.astenums; |
| |
| /****************************************************************************** |
| * 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.extractChars(); |
| } |
| 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, t); |
| 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); |
| } |
| |
| extern (C++) void mangleToBuffer(TemplateInstance ti, OutBuffer* buf) |
| { |
| scope Mangler v = new Mangler(buf); |
| v.mangleTemplateInstance(ti); |
| } |
| |
| /// Returns: `true` if the given character is a valid mangled character |
| package bool isValidMangling(dchar c) nothrow |
| { |
| return |
| c >= 'A' && c <= 'Z' || |
| c >= 'a' && c <= 'z' || |
| c >= '0' && c <= '9' || |
| c != 0 && strchr("$%().:?@[]_", c) || |
| isUniAlpha(c); |
| } |
| |
| // valid mangled characters |
| unittest |
| { |
| assert('a'.isValidMangling); |
| assert('B'.isValidMangling); |
| assert('2'.isValidMangling); |
| assert('@'.isValidMangling); |
| assert('_'.isValidMangling); |
| } |
| |
| // invalid mangled characters |
| unittest |
| { |
| assert(!'-'.isValidMangling); |
| assert(!0.isValidMangling); |
| assert(!'/'.isValidMangling); |
| assert(!'\\'.isValidMangling); |
| } |
| |
| /********************************************** |
| * Convert a string representing a type (the deco) and |
| * return its equivalent Type. |
| * Params: |
| * deco = string containing the deco |
| * Returns: |
| * null for failed to convert |
| * Type for succeeded |
| */ |
| |
| public Type decoToType(const(char)[] deco) |
| { |
| //printf("decoToType(): %.*s\n", cast(int)deco.length, deco.ptr); |
| if (auto sv = Type.stringtable.lookup(deco)) |
| { |
| if (sv.value) |
| { |
| Type t = cast(Type)sv.value; |
| assert(t.deco); |
| return t; |
| } |
| } |
| return null; |
| } |
| |
| |
| /***************************************** private ***************************************/ |
| |
| private: |
| |
| |
| import core.stdc.ctype; |
| import core.stdc.stdio; |
| import core.stdc.string; |
| |
| import dmd.aggregate; |
| import dmd.arraytypes; |
| import dmd.dclass; |
| import dmd.declaration; |
| import dmd.dmodule; |
| import dmd.dsymbol; |
| import dmd.dtemplate; |
| import dmd.expression; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.mtype; |
| import dmd.root.ctfloat; |
| import dmd.common.outbuffer; |
| import dmd.root.aav; |
| import dmd.root.string; |
| import dmd.root.stringtable; |
| import dmd.root.utf; |
| import dmd.target; |
| import dmd.tokens; |
| import dmd.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', |
| Tnull : 'n', |
| 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', |
| // I // in |
| // J // out |
| // K // ref |
| // L // lazy |
| // M // has this, or scope |
| // N // Nh:vector Ng:wild Nn:noreturn |
| // O // shared |
| Tpointer : 'P', |
| // Q // Type/symbol/identifier backward reference |
| Treference : 'R', |
| Tstruct : 'S', |
| // T // Ttypedef |
| // U // C 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 |
| Tnone : '@', |
| Tident : '@', |
| Tinstance : '@', |
| Terror : '@', |
| Ttypeof : '@', |
| Tslice : '@', |
| Treturn : '@', |
| Tvector : '@', |
| Ttraits : '@', |
| Tmixin : '@', |
| Ttag : '@', |
| Tnoreturn : '@', // becomes 'Nn' |
| ]; |
| |
| unittest |
| { |
| foreach (i, mangle; mangleChar) |
| { |
| if (mangle == char.init) |
| { |
| fprintf(stderr, "ty = %u\n", cast(uint)i); |
| assert(0); |
| } |
| } |
| } |
| |
| private extern (C++) final class Mangler : Visitor |
| { |
| alias visit = Visitor.visit; |
| public: |
| static assert(Key.sizeof == size_t.sizeof); |
| |
| OutBuffer* buf; |
| Backref backref; |
| |
| extern (D) this(OutBuffer* buf, Type rootType = null) |
| { |
| this.buf = buf; |
| this.backref = Backref(rootType); |
| } |
| |
| void mangleSymbol(Dsymbol s) |
| { |
| s.accept(this); |
| } |
| |
| void mangleType(Type t) |
| { |
| if (!backref.addRefToType(buf, t)) |
| t.accept(this); |
| } |
| |
| void mangleIdentifier(Identifier id, Dsymbol s) |
| { |
| if (!backref.addRefToIdentifier(buf, id)) |
| toBuffer(buf, id.toString(), s); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /************************************************** |
| * Type mangling |
| */ |
| void visitWithMask(Type t, ubyte modMask) |
| { |
| if (modMask != t.mod) |
| { |
| MODtoDecoBuffer(buf, t.mod); |
| } |
| mangleType(t); |
| } |
| |
| 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.print(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 && tret) |
| { |
| // printf("TypeFunction.mangleFuncType() t = %s inuse\n", t.toChars()); |
| t.inuse = 2; // flag error to caller |
| return; |
| } |
| t.inuse++; |
| if (modMask != t.mod) |
| MODtoDecoBuffer(buf, t.mod); |
| |
| char mc; |
| final switch (t.linkage) |
| { |
| case LINK.default_: |
| case LINK.d: |
| mc = 'F'; |
| break; |
| case LINK.c: |
| mc = 'U'; |
| break; |
| case LINK.windows: |
| mc = 'W'; |
| break; |
| case LINK.cpp: |
| mc = 'R'; |
| break; |
| case LINK.objc: |
| mc = 'Y'; |
| break; |
| case LINK.system: |
| assert(0); |
| } |
| buf.writeByte(mc); |
| |
| 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"); |
| |
| // `return scope` must be in that order |
| if (ta.isreturnscope && !ta.isreturninferred) |
| { |
| buf.writestring("NjNl"); |
| } |
| else |
| { |
| // when return ref, the order is `scope return` |
| if (ta.isScopeQual && !ta.isscopeinferred) |
| buf.writestring("Nl"); |
| |
| if (ta.isreturn && !ta.isreturninferred) |
| buf.writestring("Nj"); |
| } |
| |
| if (ta.islive) |
| buf.writestring("Nm"); |
| |
| switch (ta.trust) |
| { |
| case TRUST.trusted: |
| buf.writestring("Ne"); |
| break; |
| case TRUST.safe: |
| buf.writestring("Nf"); |
| break; |
| default: |
| break; |
| } |
| |
| // Write argument types |
| foreach (idx, param; t.parameterList) |
| mangleParameter(param); |
| //if (buf.data[buf.length - 1] == '@') assert(0); |
| buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list |
| if (tret !is null) |
| visitWithMask(tret, 0); |
| t.inuse--; |
| } |
| |
| override void visit(TypeIdentifier t) |
| { |
| visit(cast(Type)t); |
| auto name = t.ident.toString(); |
| buf.print(cast(int)name.length); |
| buf.writestring(name); |
| } |
| |
| override void visit(TypeEnum t) |
| { |
| visit(cast(Type)t); |
| mangleSymbol(t.sym); |
| } |
| |
| override void visit(TypeStruct t) |
| { |
| //printf("TypeStruct.toDecoBuffer('%s') = '%s'\n", t.toChars(), name); |
| visit(cast(Type)t); |
| mangleSymbol(t.sym); |
| } |
| |
| override void visit(TypeClass t) |
| { |
| //printf("TypeClass.toDecoBuffer('%s' mod=%x) = '%s'\n", t.toChars(), mod, name); |
| visit(cast(Type)t); |
| mangleSymbol(t.sym); |
| } |
| |
| override void visit(TypeTuple t) |
| { |
| //printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars()); |
| visit(cast(Type)t); |
| Parameter._foreach(t.arguments, (idx, param) { |
| mangleParameter(param); |
| return 0; |
| }); |
| buf.writeByte('Z'); |
| } |
| |
| override void visit(TypeNull t) |
| { |
| visit(cast(Type)t); |
| } |
| |
| override void visit(TypeNoreturn t) |
| { |
| buf.writestring("Nn"); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| void mangleDecl(Declaration sthis) |
| { |
| mangleParent(sthis); |
| assert(sthis.ident); |
| mangleIdentifier(sthis.ident, sthis); |
| if (FuncDeclaration fd = sthis.isFuncDeclaration()) |
| { |
| mangleFunc(fd, false); |
| } |
| else if (sthis.type) |
| { |
| visitWithMask(sthis.type, 0); |
| } |
| else |
| assert(0); |
| } |
| |
| void mangleParent(Dsymbol s) |
| { |
| //printf("mangleParent() %s %s\n", s.kind(), s.toChars()); |
| Dsymbol p; |
| if (TemplateInstance ti = s.isTemplateInstance()) |
| p = ti.isTemplateMixin() ? ti.parent : ti.tempdecl.parent; |
| else |
| p = s.parent; |
| if (p) |
| { |
| uint localNum = s.localNum; |
| mangleParent(p); |
| auto ti = p.isTemplateInstance(); |
| if (ti && !ti.isTemplateMixin()) |
| { |
| localNum = ti.tempdecl.localNum; |
| mangleTemplateInstance(ti); |
| } |
| else if (p.getIdent()) |
| { |
| mangleIdentifier(p.ident, s); |
| if (FuncDeclaration f = p.isFuncDeclaration()) |
| mangleFunc(f, true); |
| } |
| else |
| buf.writeByte('0'); |
| |
| if (localNum) |
| writeLocalParent(buf, localNum); |
| } |
| } |
| |
| 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 (!fd.type || fd.type.ty == Terror) |
| { |
| // never should have gotten here, but could be the result of |
| // failed speculative compilation |
| buf.writestring("9__error__FZ"); |
| |
| //printf("[%s] %s no type\n", fd.loc.toChars(), fd.toChars()); |
| //assert(0); // don't mangle function until semantic3 done. |
| } |
| else if (inParent) |
| { |
| TypeFunction tf = fd.type.isTypeFunction(); |
| TypeFunction tfo = fd.originalType.isTypeFunction(); |
| mangleFuncType(tf, tfo, 0, null); |
| } |
| else |
| { |
| visitWithMask(fd.type, 0); |
| } |
| } |
| |
| 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 (const id = externallyMangledIdentifier(d)) |
| { |
| buf.writestring(id); |
| return; |
| } |
| buf.writestring("_D"); |
| mangleDecl(d); |
| debug |
| { |
| const slice = (*buf)[]; |
| assert(slice.length); |
| for (size_t pos; pos < slice.length; ) |
| { |
| dchar c; |
| auto ppos = pos; |
| const s = utf_decodeChar(slice, pos, c); |
| assert(s is null, s); |
| assert(c.isValidMangling, "The mangled name '" ~ slice ~ "' " ~ |
| "contains an invalid character: " ~ slice[ppos..pos]); |
| } |
| } |
| } |
| |
| /****************************************************************************** |
| * 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) |
| { |
| mangleSymbol(fa); |
| 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 (fd.isUnique()) |
| { |
| mangleExact(fd); |
| return; |
| } |
| } |
| if (TemplateDeclaration td = od.aliassym.isTemplateDeclaration()) |
| { |
| if (td.overnext is null) |
| { |
| mangleSymbol(td); |
| 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()) |
| { |
| buf.writestring(fd.ident.toString()); |
| 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); |
| |
| if (ti.isTemplateMixin() && ti.ident) |
| mangleIdentifier(ti.ident, ti); |
| else |
| mangleTemplateInstance(ti); |
| } |
| |
| void mangleTemplateInstance(TemplateInstance ti) |
| { |
| TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration(); |
| assert(tempdecl); |
| |
| // Use "__U" for the symbols declared inside template constraint. |
| const char T = ti.members ? 'T' : 'U'; |
| buf.printf("__%c", T); |
| mangleIdentifier(tempdecl.ident, tempdecl); |
| |
| auto args = ti.tiargs; |
| size_t nparams = tempdecl.parameters.dim - (tempdecl.isVariadic() ? 1 : 0); |
| for (size_t i = 0; i < args.dim; i++) |
| { |
| auto o = (*args)[i]; |
| Type ta = isType(o); |
| Expression ea = isExpression(o); |
| Dsymbol sa = isDsymbol(o); |
| Tuple va = isTuple(o); |
| //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va); |
| if (i < nparams && (*tempdecl.parameters)[i].specialization()) |
| buf.writeByte('H'); // https://issues.dlang.org/show_bug.cgi?id=6574 |
| if (ta) |
| { |
| buf.writeByte('T'); |
| visitWithMask(ta, 0); |
| } |
| else if (ea) |
| { |
| // Don't interpret it yet, it might actually be an alias template parameter. |
| // Only constfold manifest constants, not const/immutable lvalues, see https://issues.dlang.org/show_bug.cgi?id=17339. |
| enum keepLvalue = true; |
| ea = ea.optimize(WANTvalue, keepLvalue); |
| if (auto ev = ea.isVarExp()) |
| { |
| sa = ev.var; |
| ea = null; |
| goto Lsa; |
| } |
| if (auto et = ea.isThisExp()) |
| { |
| sa = et.var; |
| ea = null; |
| goto Lsa; |
| } |
| if (auto ef = ea.isFuncExp()) |
| { |
| if (ef.td) |
| sa = ef.td; |
| else |
| sa = ef.fd; |
| ea = null; |
| goto Lsa; |
| } |
| buf.writeByte('V'); |
| if (ea.op == EXP.tuple) |
| { |
| ea.error("tuple is not a valid template value argument"); |
| continue; |
| } |
| // Now that we know it is not an alias, we MUST obtain a value |
| uint olderr = global.errors; |
| ea = ea.ctfeInterpret(); |
| if (ea.op == EXP.error || olderr != global.errors) |
| continue; |
| |
| /* Use type mangling that matches what it would be for a function parameter |
| */ |
| visitWithMask(ea.type, 0); |
| ea.accept(this); |
| } |
| else if (sa) |
| { |
| Lsa: |
| sa = sa.toAlias(); |
| if (sa.isDeclaration() && !sa.isOverDeclaration()) |
| { |
| Declaration d = sa.isDeclaration(); |
| |
| if (auto fad = d.isFuncAliasDeclaration()) |
| d = fad.toAliasFunc(); |
| if (d.mangleOverride) |
| { |
| buf.writeByte('X'); |
| toBuffer(buf, d.mangleOverride, d); |
| continue; |
| } |
| if (const id = externallyMangledIdentifier(d)) |
| { |
| buf.writeByte('X'); |
| toBuffer(buf, id, d); |
| continue; |
| } |
| if (!d.type || !d.type.deco) |
| { |
| ti.error("forward reference of %s `%s`", d.kind(), d.toChars()); |
| continue; |
| } |
| } |
| buf.writeByte('S'); |
| mangleSymbol(sa); |
| } |
| else if (va) |
| { |
| assert(i + 1 == args.dim); // must be last one |
| args = &va.objects; |
| i = -cast(size_t)1; |
| } |
| else |
| assert(0); |
| } |
| buf.writeByte('Z'); |
| } |
| |
| 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"); |
| } |
| if (s.parent && s.ident) |
| { |
| if (auto m = s.parent.isModule()) |
| { |
| if (m.filetype == FileType.c) |
| { |
| /* C types at global level get mangled into the __C global namespace |
| * to get the same mangling regardless of which module it |
| * is declared in. This works because types are the same if the mangling |
| * is the same. |
| */ |
| mangleIdentifier(Id.ImportC, s); // parent |
| mangleIdentifier(s.ident, s); |
| return; |
| } |
| } |
| } |
| mangleParent(s); |
| if (s.ident) |
| mangleIdentifier(s.ident, s); |
| else |
| toBuffer(buf, s.toString(), 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) |
| { |
| const v = e.toInteger(); |
| if (cast(sinteger_t)v < 0) |
| { |
| buf.writeByte('N'); |
| buf.print(-v); |
| } |
| else |
| { |
| buf.writeByte('i'); |
| buf.print(v); |
| } |
| } |
| |
| override void visit(RealExp e) |
| { |
| buf.writeByte('e'); |
| realToMangleBuffer(buf, e.value); |
| } |
| |
| override void visit(ComplexExp e) |
| { |
| buf.writeByte('c'); |
| realToMangleBuffer(buf, e.toReal()); |
| buf.writeByte('c'); // separate the two |
| realToMangleBuffer(buf, 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.peekString(); |
| break; |
| case 2: |
| { |
| m = 'w'; |
| const slice = e.peekWstring(); |
| for (size_t u = 0; u < e.len;) |
| { |
| dchar c; |
| if (const s = utf_decodeWchar(slice, u, c)) |
| e.error("%.*s", cast(int)s.length, s.ptr); |
| else |
| tmp.writeUTF8(c); |
| } |
| q = tmp[]; |
| break; |
| } |
| case 4: |
| { |
| m = 'd'; |
| const slice = e.peekDstring(); |
| foreach (c; slice) |
| { |
| if (!utf_isValidDchar(c)) |
| e.error("invalid UCS-32 char \\U%08x", c); |
| else |
| tmp.writeUTF8(c); |
| } |
| q = tmp[]; |
| break; |
| } |
| |
| default: |
| assert(0); |
| } |
| buf.reserve(1 + 11 + 2 * q.length); |
| buf.writeByte(m); |
| buf.print(q.length); |
| buf.writeByte('_'); // nbytes <= 11 |
| auto slice = buf.allocate(2 * q.length); |
| foreach (i, c; q) |
| { |
| char hi = (c >> 4) & 0xF; |
| slice[i * 2] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + 'a'); |
| char lo = c & 0xF; |
| slice[i * 2 + 1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + 'a'); |
| } |
| } |
| |
| override void visit(ArrayLiteralExp e) |
| { |
| const dim = e.elements ? e.elements.dim : 0; |
| buf.writeByte('A'); |
| buf.print(dim); |
| foreach (i; 0 .. dim) |
| { |
| e[i].accept(this); |
| } |
| } |
| |
| override void visit(AssocArrayLiteralExp e) |
| { |
| const dim = e.keys.dim; |
| buf.writeByte('A'); |
| buf.print(dim); |
| foreach (i; 0 .. dim) |
| { |
| (*e.keys)[i].accept(this); |
| (*e.values)[i].accept(this); |
| } |
| } |
| |
| override void visit(StructLiteralExp e) |
| { |
| const dim = e.elements ? e.elements.dim : 0; |
| buf.writeByte('S'); |
| buf.print(dim); |
| foreach (i; 0 .. dim) |
| { |
| Expression ex = (*e.elements)[i]; |
| if (ex) |
| ex.accept(this); |
| else |
| buf.writeByte('v'); // 'v' for void |
| } |
| } |
| |
| override void visit(FuncExp e) |
| { |
| buf.writeByte('f'); |
| if (e.td) |
| mangleSymbol(e.td); |
| else |
| mangleSymbol(e.fd); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| void mangleParameter(Parameter p) |
| { |
| // https://dlang.org/spec/abi.html#Parameter |
| |
| auto stc = p.storageClass; |
| |
| // Inferred storage classes don't get mangled in |
| if (stc & STC.scopeinferred) |
| stc &= ~(STC.scope_ | STC.scopeinferred); |
| if (stc & STC.returninferred) |
| stc &= ~(STC.return_ | STC.returninferred); |
| |
| // much like hdrgen.stcToBuffer() |
| string rrs; |
| const isout = (stc & STC.out_) != 0; |
| final switch (buildScopeRef(stc)) |
| { |
| case ScopeRef.None: |
| case ScopeRef.Scope: |
| case ScopeRef.Ref: |
| case ScopeRef.Return: |
| case ScopeRef.RefScope: |
| break; |
| |
| case ScopeRef.ReturnScope: rrs = "NkM"; goto L1; // return scope |
| case ScopeRef.ReturnRef: rrs = isout ? "NkJ" : "NkK"; goto L1; // return ref |
| case ScopeRef.ReturnRef_Scope: rrs = isout ? "MNkJ" : "MNkK"; goto L1; // scope return ref |
| case ScopeRef.Ref_ReturnScope: rrs = isout ? "NkMJ" : "NkMK"; goto L1; // return scope ref |
| L1: |
| buf.writestring(rrs); |
| stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_); |
| break; |
| } |
| |
| if (stc & STC.scope_) |
| buf.writeByte('M'); // scope |
| |
| if (stc & STC.return_) |
| buf.writestring("Nk"); // return |
| |
| switch (stc & (STC.IOR | STC.lazy_)) |
| { |
| case 0: |
| break; |
| case STC.in_: |
| buf.writeByte('I'); |
| break; |
| case STC.in_ | STC.ref_: |
| buf.writestring("IK"); |
| break; |
| case STC.out_: |
| buf.writeByte('J'); |
| break; |
| case STC.ref_: |
| buf.writeByte('K'); |
| break; |
| case STC.lazy_: |
| buf.writeByte('L'); |
| break; |
| default: |
| debug |
| { |
| printf("storageClass = x%llx\n", stc & (STC.IOR | STC.lazy_)); |
| } |
| assert(0); |
| } |
| visitWithMask(p.type, (stc & STC.in_) ? MODFlags.const_ : 0); |
| } |
| } |
| |
| /*************************************** |
| * Manage back reference mangling |
| */ |
| private struct Backref |
| { |
| /** |
| * Back references a non-basic type |
| * |
| * The encoded mangling is |
| * 'Q' <relative position of first occurrence of type> |
| * |
| * Params: |
| * t = the type to encode via back referencing |
| * |
| * Returns: |
| * true if the type was found. A back reference has been encoded. |
| * false if the type was not found. The current position is saved for later back references. |
| */ |
| bool addRefToType(OutBuffer* buf, Type t) |
| { |
| if (t.isTypeBasic()) |
| return false; |
| |
| /** |
| * https://issues.dlang.org/show_bug.cgi?id=21591 |
| * |
| * Special case for unmerged TypeFunctions: use the generic merged |
| * function type as backref cache key to avoid missed backrefs. |
| * |
| * Merging is based on mangling, so we need to avoid an infinite |
| * recursion by excluding the case where `t` is the root type passed to |
| * `mangleToBuffer()`. |
| */ |
| if (t != rootType) |
| { |
| if (t.isFunction_Delegate_PtrToFunction()) |
| { |
| t = t.merge2(); |
| } |
| } |
| |
| return backrefImpl(buf, types, t); |
| } |
| |
| /** |
| * Back references a single identifier |
| * |
| * The encoded mangling is |
| * 'Q' <relative position of first occurrence of type> |
| * |
| * Params: |
| * id = the identifier to encode via back referencing |
| * |
| * Returns: |
| * true if the identifier was found. A back reference has been encoded. |
| * false if the identifier was not found. The current position is saved for later back references. |
| */ |
| bool addRefToIdentifier(OutBuffer* buf, Identifier id) |
| { |
| return backrefImpl(buf, idents, id); |
| } |
| |
| private: |
| |
| extern(D) bool backrefImpl(T)(OutBuffer* buf, ref AssocArray!(T, size_t) aa, T key) |
| { |
| auto p = aa.getLvalue(key); |
| if (*p) |
| { |
| const offset = *p - 1; |
| writeBackRef(buf, buf.length - offset); |
| return true; |
| } |
| *p = buf.length + 1; |
| return false; |
| } |
| |
| Type rootType; /// avoid infinite recursion |
| AssocArray!(Type, size_t) types; /// Type => (offset+1) in buf |
| AssocArray!(Identifier, size_t) idents; /// Identifier => (offset+1) in buf |
| } |
| |
| |
| /*********************** |
| * 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 MODFlags.const_: |
| buf.writeByte('x'); |
| break; |
| case MODFlags.immutable_: |
| buf.writeByte('y'); |
| break; |
| case MODFlags.shared_: |
| buf.writeByte('O'); |
| break; |
| case MODFlags.shared_ | MODFlags.const_: |
| buf.writestring("Ox"); |
| break; |
| case MODFlags.wild: |
| buf.writestring("Ng"); |
| break; |
| case MODFlags.wildconst: |
| buf.writestring("Ngx"); |
| break; |
| case MODFlags.shared_ | MODFlags.wild: |
| buf.writestring("ONg"); |
| break; |
| case MODFlags.shared_ | MODFlags.wildconst: |
| buf.writestring("ONgx"); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| |
| /** |
| * writes a back reference with the relative position encoded with base 26 |
| * using upper case letters for all digits but the last digit which uses |
| * a lower case letter. |
| * The decoder has to look up the referenced position to determine |
| * whether the back reference is an identifier (starts with a digit) |
| * or a type (starts with a letter). |
| * |
| * Params: |
| * buf = buffer to write to |
| * pos = relative position to encode |
| */ |
| private |
| void writeBackRef(OutBuffer* buf, size_t pos) |
| { |
| buf.writeByte('Q'); |
| enum base = 26; |
| size_t mul = 1; |
| while (pos >= mul * base) |
| mul *= base; |
| while (mul >= base) |
| { |
| auto dig = cast(ubyte)(pos / mul); |
| buf.writeByte('A' + dig); |
| pos -= dig * mul; |
| mul /= base; |
| } |
| buf.writeByte('a' + cast(ubyte)pos); |
| } |
| |
| |
| /************************************************************ |
| * Write length prefixed string to buf. |
| */ |
| private |
| extern (D) void toBuffer(OutBuffer* buf, const(char)[] id, Dsymbol s) |
| { |
| const len = id.length; |
| if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone |
| s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len)); |
| else |
| { |
| buf.print(len); |
| buf.writestring(id); |
| } |
| } |
| |
| |
| /***** |
| * There can be multiple different declarations in the same |
| * function that have the same mangled name. |
| * This results in localNum having a non-zero number, which |
| * is used to add a fake parent of the form `__Sddd` to make |
| * the mangled names unique. |
| * https://issues.dlang.org/show_bug.cgi?id=20565 |
| * Params: |
| * buf = buffer to write to |
| * localNum = local symbol number |
| */ |
| private |
| void writeLocalParent(OutBuffer* buf, uint localNum) |
| { |
| uint ndigits = 1; |
| auto n = localNum; |
| while (n >= 10) |
| { |
| n /= 10; |
| ++ndigits; |
| } |
| buf.printf("%u__S%u", ndigits + 3, localNum); |
| } |
| |
| /************************* |
| * Write real to buffer. |
| * Params: |
| * buf = buffer to write to |
| * value = real to write |
| */ |
| private |
| void realToMangleBuffer(OutBuffer* buf, 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 |
| return; |
| } |
| |
| if (value < CTFloat.zero) |
| { |
| buf.writeByte('N'); |
| value = -value; |
| } |
| |
| if (CTFloat.isInfinity(value)) |
| { |
| buf.writestring("INF"); |
| return; |
| } |
| |
| char[36] buffer = void; |
| // 'A' format yields [-]0xh.hhhhp+-d |
| const n = CTFloat.sprint(buffer.ptr, 'A', value); |
| assert(n < buffer.length); |
| foreach (const c; buffer[2 .. n]) |
| { |
| switch (c) |
| { |
| case '-': |
| buf.writeByte('N'); |
| break; |
| |
| case '+': |
| case '.': |
| break; |
| |
| default: |
| buf.writeByte(c); |
| break; |
| } |
| } |
| } |
| |
| /************************************************************ |
| * Try to obtain an externally mangled identifier from a declaration. |
| * If the declaration is at global scope or mixed in at global scope, |
| * the user might want to call it externally, so an externally mangled |
| * name is returned. Member functions or nested functions can't be called |
| * externally in C, so in that case null is returned. C++ does support |
| * namespaces, so extern(C++) always gives a C++ mangled name. |
| * |
| * See also: https://issues.dlang.org/show_bug.cgi?id=20012 |
| * |
| * Params: |
| * d = declaration to mangle |
| * |
| * Returns: |
| * an externally mangled name or null if the declaration cannot be called externally |
| */ |
| private |
| extern (D) const(char)[] externallyMangledIdentifier(Declaration d) |
| { |
| assert(!d.mangleOverride, "mangle overrides should have been handled earlier"); |
| |
| const linkage = d.resolvedLinkage(); |
| const par = d.toParent(); //toParent() skips over mixin templates |
| if (!par || par.isModule() || linkage == LINK.cpp || |
| (linkage == LINK.c && d.isCsymbol() && |
| (d.isFuncDeclaration() || |
| (d.isVarDeclaration() && d.isDataseg() && d.storage_class & STC.extern_)))) |
| { |
| if (linkage != LINK.d && d.localNum) |
| d.error("the same declaration cannot be in multiple scopes with non-D linkage"); |
| |
| final switch (linkage) |
| { |
| case LINK.d: |
| break; |
| case LINK.c: |
| case LINK.windows: |
| case LINK.objc: |
| return d.ident.toString(); |
| case LINK.cpp: |
| { |
| const p = target.cpp.toMangle(d); |
| return p.toDString(); |
| } |
| case LINK.default_: |
| d.error("forward declaration"); |
| return d.ident.toString(); |
| case LINK.system: |
| assert(0); |
| } |
| } |
| return null; |
| } |