1 /** ELF Symbol Name (De)Mangling.
2     Copyright: Per Nordlöw 2018-.
3     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4     Authors: $(WEB Per Nordlöw)
5     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html
6 
7     TODO Only check for emptyness before any optionals.
8 
9     TODO Search for pattern "X> <Y" and assure that they all use
10     return r.tryEvery(X, Y).
11 
12     TODO 1. Replace calls to decode ~ decode with separate decodes
13     TODO 2 : Replace calls to decode ~ decode with a sequence call.
14 
15     TODO Detect recursion:
16           See: http://forum.dlang.org/thread/edaduxaxmihvzkoudeqa@forum.dlang.org#post-edaduxaxmihvzkoudeqa:40forum.dlang.org
17           See: http://code.dlang.org/packages/backtrace-d
18 
19     TODO What role does _ZL have? See localFlag for details.
20  */
21 module nxt.mangling;
22 
23 import std.range.primitives: empty, front, popFront, moveFront, popFrontExactly, isInputRange;
24 import std.algorithm: joiner, min;
25 import std.conv: to;
26 import std.ascii: isDigit;
27 
28 import std.algorithm.comparison : either;
29 import nxt.array_algorithm : startsWith, skipOver;
30 import nxt.algorithm_ex: tryEvery, split, splitBefore;
31 import nxt.languages : Lang;
32 import nxt.dbgio : dbg;
33 
34 /** C++ Demangler. */
35 class Demangler(R)
36 if (isInputRange!R)
37 {
38     this(R r,
39          bool explicitVoidParameter = false,
40          bool show = false)
41     {
42         this.r = r;
43         this.explicitVoidParameter = explicitVoidParameter;
44         this.show = show;
45     }
46     R r;
47     bool show = false;
48     bool explicitVoidParameter = false; // set to true make void parameters explicit
49 private:
50     string[] sourceNames;
51     CxxType[] ids; // ids demangled so far
52     R scopeSeparator = "::";
53 }
54 auto demangler(T...)(T args)
55 if (isInputRange!(T[0]))
56 {
57     return new Demangler!(T[0])(args);
58 }
59 
60 /** Like $(D skipOver) but return $(D string) instead of $(D bool).
61     Bool-conversion of returned value gives same result as r.skipOver(lit).
62 */
63 string skipLiteral(R, E)(Demangler!R x, E lit)
64 if (isInputRange!R)
65 {
66     return x.r.skipOver(lit) ? "" : null;
67 }
68 
69 /** Decode Unqualified C++ Type at $(D r).
70     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type
71 */
72 R decodeCxxUnqualifiedType(R)(Demangler!R x)
73 if (isInputRange!R)
74 {
75     if (x.show) dbg("rest: ", x.r);
76     return either(x.decodeCxxBuiltinType(),
77                   x.decodeCxxSubstitution(),
78                   x.decodeCxxFunctionType());
79 }
80 
81 struct CxxType
82 {
83     string typeName;
84     bool isRef = false;      // & <ref-qualifier>
85     bool isRvalueRef = false;    // && ref-qualifier (C++11)
86     bool isComplexPair = false;    // complex pair (C 2000)
87     bool isImaginary = false;    // imaginary (C 2000)
88     byte pointyness = 0;           // pointer level
89     CXXCVQualifiers cvQ;
90 
91     @property void toString(scope void delegate(scope const(char)[]) sink) const
92     {
93         if (cvQ.isVolatile) { sink("volatile "); }
94         if (cvQ.isRestrict) { sink("restrict "); }
95 
96         sink(typeName);
97 
98         if (cvQ.isConst) { sink(" const"); }
99 
100         // suffix qualifiers
101         foreach (immutable _; 0 .. pointyness)
102         {
103             sink(`*`);// str ~= "*".replicate(pointyness);
104         }
105         if (isRef) { sink(`&`); }
106         if (isRvalueRef) { sink(`&&`); }
107     }
108 }
109 
110 /** Decode C++ Type at $(D r).
111     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type
112 */
113 R decodeCxxType(R)(Demangler!R x)
114 if (isInputRange!R)
115 {
116     if (x.show) dbg("rest: ", x.r);
117 
118     const packExpansion = x.r.skipOver(`Dp`); // (C++11)
119 
120     CxxType cxxType;
121 
122     while (!x.r.empty)
123     {
124         if (const cvQ_ = x.r.decodeCxxCVQualifiers()) // TODO Optimize
125         {
126             cxxType.cvQ.isRestrict |= cvQ_.isRestrict;
127             cxxType.cvQ.isVolatile |= cvQ_.isVolatile;
128             cxxType.cvQ.isConst |= cvQ_.isConst;
129             continue;
130         }
131         else
132         {
133             auto miss = false;
134             switch (x.r[0])
135             {
136                 case 'P': x.r.popFront(); cxxType.pointyness++; break;
137                     // <ref-qualifier>: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier
138                 case 'R': x.r.popFront(); cxxType.isRef = true; break;
139                 case 'O': x.r.popFront(); cxxType.isRvalueRef = true; break;
140                 case 'C': x.r.popFront(); cxxType.isComplexPair = true; dbg("TODO Handle complex pair (C 2000)"); break;
141                 case 'G': x.r.popFront(); cxxType.isImaginary = true; dbg("TODO Handle imaginary (C 2000)"); break;
142                 case 'U': x.r.popFront();
143                     const sourceName = x.decodeCxxSourceName();
144                     cxxType.typeName = sourceName ~ x.decodeCxxType();
145                     dbg("TODO Handle vendor extended type qualifier <source-name>", x.r);
146                     break;
147                 default: miss = true; break;
148             }
149             if (miss)
150             {
151                 break;
152             }
153         }
154     }
155     assert(!(cxxType.isRef && cxxType.isRvalueRef));
156 
157     if (x.r.empty) { return cxxType.to!string; }
158 
159     cxxType.typeName = either(x.decodeCxxBuiltinType(),
160                               x.decodeCxxFunctionType(),
161                               x.decodeCxxClassEnumType(),
162                               x.decodeCxxArrayType(),
163                               x.decodeCxxPointerToMemberType(),
164                               x.decodeCxxTemplateTemplateParamAndArgs(),
165                               x.decodeCxxDecltype(),
166                               x.decodeCxxSubstitution());
167 
168     x.ids ~= cxxType;
169 
170     return cxxType.to!string;
171 }
172 
173 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.class-enum-type */
174 R decodeCxxClassEnumType(R)(Demangler!R x)
175 if (isInputRange!R)
176 {
177     if (x.show) dbg("rest: ", x.r);
178     R type;
179     R prefix;
180     enum n = 2;
181     if (x.r.length >= n)
182     {
183         switch (x.r[0..n])
184         {
185             case `Ts`: prefix = `struct `; break;
186             case `Tu`: prefix = `union `; break;
187             case `Te`: prefix = `enum `; break;
188             default: break;
189         }
190         if (prefix)
191         {
192             x.r.popFrontExactly(n);
193         }
194     }
195     const name = x.decodeCxxName();
196     if (name)
197     {
198         type = prefix ~ name;
199     }
200     else
201     {
202         assert(!prefix); // if we failed to decode name prefix should not have existed either
203     }
204     return type;
205 }
206 
207 R decodeCxxExpression(R)(Demangler!R x)
208 if (isInputRange!R)
209 {
210     if (x.show) dbg("rest: ", x.r);
211     R exp;
212     assert(0, "TODO");
213 }
214 
215 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.array-type */
216 R decodeCxxArrayType(R)(Demangler!R x)
217 if (isInputRange!R)
218 {
219     if (x.show) dbg("rest: ", x.r);
220     R type;
221     if (x.r.skipOver('A'))
222     {
223         if (const num = x.decodeCxxNumber())
224         {
225             assert(x.r.skipOver('_'));
226             type = x.decodeCxxType() ~ `[]` ~ num ~ `[]`;
227         }
228         else
229         {
230             const dimensionExpression = x.decodeCxxExpression();
231             assert(x.r.skipOver('_'));
232             type = x.decodeCxxType() ~ `[]` ~ dimensionExpression ~ `[]`;
233         }
234     }
235     return type;
236 }
237 
238 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.pointer-to-member-type */
239 R decodeCxxPointerToMemberType(R)(Demangler!R x)
240 if (isInputRange!R)
241 {
242     if (x.show) dbg("rest: ", x.r);
243     R type;
244     if (x.r.skipOver('M'))
245     {
246         const classType = x.decodeCxxType(); // <class type>
247         const memberType = x.decodeCxxType(); // <mmeber type>
248         type = classType ~ memberType;
249     }
250     return type;
251 }
252 
253 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-param */
254 R decodeCxxTemplateParam(R)(Demangler!R x)
255 if (isInputRange!R)
256 {
257     if (x.show) dbg("rest: ", x.r);
258     R param;
259     if (x.r.skipOver('T'))
260     {
261         if (x.r.skipOver('_'))
262         {
263             param = `first template parameter`;
264         }
265         else
266         {
267             param = x.decodeCxxNumber();
268             assert(x.r.skipOver('_'));
269         }
270     }
271     return param;
272 }
273 
274 R decodeCxxTemplateTemplateParamAndArgs(R)(Demangler!R x)
275 if (isInputRange!R)
276 {
277     if (x.show) dbg("rest: ", x.r);
278     R value;
279     if (const param = either(x.decodeCxxTemplateParam(),
280                              x.decodeCxxSubstitution()))
281     {
282         auto args = x.decodeCxxTemplateArgs();
283         value = param ~ args.joiner(`, `).to!R;
284     }
285     return value;
286 }
287 
288 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.decltype */
289 R decodeCxxDecltype(R)(Demangler!R x)
290 if (isInputRange!R)
291 {
292     if (x.show) dbg("rest: ", x.r);
293     R type;
294     if (x.r.skipOver(`Dt`) ||
295         x.r.skipOver(`DT`))
296     {
297         type = x.decodeCxxExpression();
298         assert(x.r.skipOver('E'));
299     }
300     return type;
301 }
302 
303 R decodeCxxDigit(R)(Demangler!R x)
304 if (isInputRange!R)
305 {
306     if (x.show) dbg("rest: ", x.r);
307     auto digit = x.r[0..1];
308     x.r.popFront();
309     return digit;
310 }
311 
312 /** Try to Decode C++ Operator at $(D r).
313     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-operator
314 */
315 R decodeCxxOperatorName(R)(Demangler!R x)
316 if (isInputRange!R)
317 {
318     if (x.show) dbg("rest: ", x.r);
319 
320     if (x.r.skipOver('v'))     // vendor extended operator
321     {
322         const digit = x.decodeCxxDigit();
323         const sourceName = x.decodeCxxSourceName();
324         return digit ~ sourceName;
325     }
326 
327     R op;
328     enum n = 2;
329     if (x.r.length < n) { return typeof(return).init; }
330     const code = x.r[0..n];
331     switch (code)
332     {
333         case `nw`: op = `operator new`; break;
334         case `na`: op = `operator new[]`; break;
335         case `dl`: op = `operator delete`; break;
336         case `da`: op = `operator delete[]`; break;
337         case `ps`: op = `operator+`; break; // unary plus
338         case `ng`: op = `operator-`; break; // unary minus
339 
340         case `ad`: op = `operator&`; break; // address of
341         case `de`: op = `operator*`; break; // dereference
342 
343         case `co`: op = `operator~`; break; // bitwise complement
344         case `pl`: op = `operator+`; break; // plus
345         case `mi`: op = `operator-`; break; // minus
346 
347         case `ml`: op = `operator*`; break; // multiplication
348         case `dv`: op = `operator/`; break; // division
349         case `rm`: op = `operator%`; break; // remainder
350 
351         case `an`: op = `operator&`; break; // bitwise and
352         case `or`: op = `operator|`; break; // bitwise of
353 
354         case `eo`: op = `operator^`; break;
355         case `aS`: op = `operator=`; break;
356 
357         case `pL`: op = `operator+=`; break;
358         case `mI`: op = `operator-=`; break;
359         case `mL`: op = `operator*=`; break;
360         case `dV`: op = `operator/=`; break;
361         case `rM`: op = `operator%=`; break;
362 
363         case `aN`: op = `operator&=`; break;
364         case `oR`: op = `operator|=`; break;
365         case `eO`: op = `operator^=`; break;
366 
367         case `ls`: op = `operator<<`; break;
368         case `rs`: op = `operator>>`; break;
369         case `lS`: op = `operator<<=`; break;
370         case `rS`: op = `operator>>=`; break;
371 
372         case `eq`: op = `operator==`; break;
373         case `ne`: op = `operator!=`; break;
374         case `lt`: op = `operator<`; break;
375         case `gt`: op = `operator>`; break;
376         case `le`: op = `operator<=`; break;
377         case `ge`: op = `operator>=`; break;
378 
379         case `nt`: op = `operator!`; break;
380         case `aa`: op = `operator&&`; break;
381         case `oo`: op = `operator||`; break;
382 
383         case `pp`: op = `operator++`; break; // (postfix in <expression> context)
384         case `mm`: op = `operator--`; break; // (postfix in <expression> context)
385 
386         case `cm`: op = `operator,`; break;
387 
388         case `pm`: op = `operator->*`; break;
389         case `pt`: op = `operator->`; break;
390 
391         case `cl`: op = `operator()`; break;
392         case `ix`: op = `operator[]`; break;
393         case `qu`: op = `operator?`; break;
394         case `cv`: op = `(cast)`; break;
395         case `li`: op = `operator""`; break;
396         default: break;
397     }
398 
399     if (op)
400     {
401         x.r.popFrontExactly(n); // digest it
402     }
403 
404     switch (code)
405     {
406         case `cv`: op = '(' ~ x.decodeCxxType() ~ ')'; break;
407         case `li`: op = (`operator ""` ~ x.decodeCxxSourceName()); break;
408         default: break;
409     }
410 
411     return op;
412 }
413 
414 /** Try to Decode C++ Builtin Type at $(D r).
415     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.builtin-type
416 */
417 R decodeCxxBuiltinType(R)(Demangler!R x)
418 if (isInputRange!R)
419 {
420     if (x.show) dbg("rest: ", x.r);
421     R type;
422     enum n = 1;
423     if (x.r.length < n) { return type; }
424     switch (x.r[0])
425     {
426         case 'v': x.r.popFront(); type = `void`; break;
427         case 'w': x.r.popFront(); type = `wchar_t`; break;
428 
429         case 'b': x.r.popFront(); type = `bool`; break;
430 
431         case 'c': x.r.popFront(); type = `char`; break;
432         case 'a': x.r.popFront(); type = `signed char`; break;
433         case 'h': x.r.popFront(); type = `unsigned char`; break;
434 
435         case 's': x.r.popFront(); type = `short`; break;
436         case 't': x.r.popFront(); type = `unsigned short`; break;
437 
438         case 'i': x.r.popFront(); type = `int`; break;
439         case 'j': x.r.popFront(); type = `unsigned int`; break;
440 
441         case 'l': x.r.popFront(); type = `long`; break;
442         case 'm': x.r.popFront(); type = `unsigned long`; break;
443 
444         case 'x': x.r.popFront(); type = `long long`; break;  // __int64
445         case 'y': x.r.popFront(); type = `unsigned long long`; break; // __int64
446 
447         case 'n': x.r.popFront(); type = `__int128`; break;
448         case 'o': x.r.popFront(); type = `unsigned __int128`; break;
449 
450         case 'f': x.r.popFront(); type = `float`; break;
451         case 'd': x.r.popFront(); type = `double`; break;
452         case 'e': x.r.popFront(); type = `long double`; break; // __float80
453         case 'g': x.r.popFront(); type = `__float128`; break;
454 
455         case 'z': x.r.popFront(); type = `...`; break; // ellipsis
456 
457         case 'D':
458             x.r.popFront();
459             assert(!x.r.empty); // need one more
460             switch (x.r[0])
461             {
462                 case 'd': x.r.popFront(); type = `IEEE 754r decimal floating point (64 bits)`; break;
463                 case 'e': x.r.popFront(); type = `IEEE 754r decimal floating point (128 bits)`; break;
464                 case 'f': x.r.popFront(); type = `IEEE 754r decimal floating point (32 bits)`; break;
465                 case 'h': x.r.popFront(); type = `IEEE 754r half-precision floating point (16 bits)`; break;
466                 case 'i': x.r.popFront(); type = `char32_t`; break;
467                 case 's': x.r.popFront(); type = `char16_t`; break;
468                 case 'a': x.r.popFront(); type = `auto`; break;
469                 case 'c': x.r.popFront(); type = `decltype(auto)`; break;
470                 case 'n': x.r.popFront(); type = `std::nullptr_t`; break; // (i.e., decltype(nullptr))
471                 default: dbg(`TODO Handle `, x.r);
472             }
473             break;
474 
475             /* TODO */
476             /* ::= u <source-name>	# vendor extended type */
477 
478         default:
479             break;
480     }
481 
482     return type;
483 }
484 
485 /** Decode C++ Substitution Type at $(D r).
486     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.substitution
487 */
488 R decodeCxxSubstitution(R)(Demangler!R x, R stdPrefix = `::std::`)
489 if (isInputRange!R)
490 {
491     if (x.show) dbg("rest: ", x.r);
492     R type;
493     if (x.r.skipOver('S'))
494     {
495         if (x.r.front == '_') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
496         {
497             type = x.ids[0].to!R;
498             x.r.popFront();
499         }
500         else if ('0' <= x.r.front && x.r.front <= '9') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
501         {
502             const ix = (x.r.front - '0');
503             auto ids_ = x.ids[min(x.ids.length - 1, ix + 1)]; // TODO Use of min here is hacky. Investigate.
504             if (ix == 0)
505             {
506                 /* NOTE: Undocumented: decrease pointyness.
507                    See for example: parse_arch(size_t argc, const char** argv, const char* arch)
508                    in dmd/src/mars.c
509                 */
510                 ids_.pointyness = ids_.pointyness >= 1 ? cast(byte)(ids_.pointyness - 1): 0;
511             }
512             type = ids_.to!R;
513             x.r.popFront();
514             x.r.skipOver('_'); // TODO Relaxed this to optional by removing surrounding assert. Investigate.
515         }
516         else if ('A' <= x.r.front && x.r.front <= 'Z') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
517         {
518             const ix = (x.r.front - 'A' + 11);
519             type = x.ids[ix].to!R;
520             x.r.popFront();
521             assert(x.r.skipOver('_'));
522         }
523         else
524         {
525             type = stdPrefix;
526             switch (x.r.front)
527             {
528                 case 't': x.r.popFront(); type ~= `ostream`; break;
529                 case 'a': x.r.popFront(); type ~= `allocator`; break;
530                 case 'b': x.r.popFront(); type ~= `basic_string`; break;
531                 case 's': x.r.popFront(); type ~= `basic_string<char, std::char_traits<char>, std::allocator<char> >`; break;
532                 case 'i': x.r.popFront(); type ~= `istream`; break;
533                 case 'o': x.r.popFront(); type ~= `ostream`; break;
534                 case 'd': x.r.popFront(); type ~= `iostream`; break;
535 
536                 default:
537                     dbg(`Cannot handle C++ standard prefix character: '`, x.r.front, `'`);
538                     x.r.popFront();
539                     break;
540             }
541         }
542     }
543     return type;
544 }
545 
546 /** Try to Decode C++ Function Type at $(D r).
547     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.function-type
548 */
549 R decodeCxxFunctionType(R)(Demangler!R x)
550 if (isInputRange!R)
551 {
552     if (x.show) dbg("rest: ", x.r);
553     auto restLookAhead = x.r; // needed for lookahead parsing of CV-qualifiers
554     const cvQ = restLookAhead.decodeCxxCVQualifiers();
555     R type;
556     if (restLookAhead.skipOver('F'))
557     {
558         x.r = restLookAhead; // we have found it
559         x.r.skipOver('Y'); // optional
560         type = x.decodeCxxBareFunctionType().to!R;
561         const refQ = x.decodeCxxRefQualifier();
562         type ~= refQ.toCxxString;
563 
564     }
565     return type;
566 }
567 
568 struct CxxBareFunctionType(R)
569 if (isInputRange!R)
570 {
571     R[] types; // optional return and parameter types
572     bool explicitVoidParameter = false; // set to true make void parameters explicit
573 
574     R toString()                // TODO use sink
575         @safe pure
576     {
577         R value;
578         if (!types.empty)
579         {
580             value ~= `(`;
581             if (this.explicitVoidParameter ||
582                 !(types.length == 1 && types.front == "void"))
583             {
584                 value ~= types.joiner(`, `).to!R;
585             }
586             value ~= `)`;
587         }
588         return value;
589     }
590 }
591 
592 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.bare-function-type */
593 CxxBareFunctionType!R decodeCxxBareFunctionType(R)(Demangler!R x)
594 if (isInputRange!R)
595 {
596     if (x.show) dbg("rest: ", x.r);
597     typeof(return) bareFunctionType;
598     bareFunctionType.explicitVoidParameter = x.explicitVoidParameter;
599 
600     /* TODO This behaviour may not follow grammar. */
601     if (const firstType = x.decodeCxxType())
602     {
603         bareFunctionType.types ~= firstType;
604     }
605 
606     while (!x.r.empty)
607     {
608         auto type = x.decodeCxxType();
609         if (type)
610         {
611             bareFunctionType.types ~= type;
612         }
613         else
614         {
615             break;
616         }
617     }
618 
619     return bareFunctionType;
620 }
621 
622 struct CXXCVQualifiers
623 {
624     bool isRestrict; // (C99)
625     bool isVolatile; // volatile
626     bool isConst; // const
627 
628     auto opCast(T : bool)()
629         @safe pure nothrow const
630     {
631         return (isRestrict ||
632                 isVolatile ||
633                 isConst);
634     }
635 
636     @property void toString(scope void delegate(scope const(char)[]) sink) const
637     {
638         if (isRestrict) sink(`restrict `);
639         if (isVolatile) sink(`volatile `);
640         if (isConst)    sink(`const `);
641     }
642 }
643 
644 /** Decode <CV-qualifiers>
645     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
646 */
647 CXXCVQualifiers decodeCxxCVQualifiers(R)(ref R r)
648 if (isInputRange!R)
649 {
650     typeof(return) cvQ;
651     if (r.skipOver('r')) { cvQ.isRestrict = true; }
652     if (r.skipOver('V')) { cvQ.isVolatile = true; }
653     if (r.skipOver('K')) { cvQ.isConst    = true; }
654     return cvQ;
655 }
656 
657 enum CxxRefQualifier
658 {
659     none,
660     normalRef,
661     rvalueRef
662 }
663 
664 /* See_Also: http://forum.dlang.org/thread/cvhapzsrhjdnpkdspavg@forum.dlang.org#post-cvhapzsrhjdnpkdspavg:40forum.dlang.org */
665 string toCxxString(CxxRefQualifier refQ)
666     @safe pure nothrow
667 {
668     final switch (refQ)
669     {
670         case CxxRefQualifier.none: return "";
671         case CxxRefQualifier.normalRef: return "&";
672         case CxxRefQualifier.rvalueRef: return "&&";
673     }
674 }
675 
676 /** Decode <ref-qualifier>
677     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier
678 */
679 CxxRefQualifier decodeCxxRefQualifier(R)(Demangler!R x)
680 if (isInputRange!R)
681 {
682     if (x.show) dbg("rest: ", x.r);
683     if (x.r.skipOver('R'))
684     {
685         return CxxRefQualifier.normalRef;
686     }
687     else if (x.r.skipOver('O'))
688     {
689         return CxxRefQualifier.rvalueRef;
690     }
691     else
692     {
693         return CxxRefQualifier.none;
694     }
695 }
696 
697 /** Decode Identifier <source-name>.
698     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.source-name
699 */
700 R decodeCxxSourceName(R)(Demangler!R x)
701 if (isInputRange!R)
702 {
703     if (x.show) dbg("rest: ", x.r);
704     R id;
705     const sign = x.r.skipOver('n'); // if negative number
706     assert(!sign);
707     const match = x.r.splitBefore!(a => !a.isDigit);
708     const digits = match[0];
709     x.r = match[1];
710     if (!digits.empty)     // digit prefix
711     {
712         // TODO Functionize these three lines
713         const num = digits.to!uint;
714         id = x.r[0..num]; // identifier, x.r.take(num)
715         x.r = x.r[num..$]; // x.r.drop(num);
716         x.sourceNames ~= id;
717     }
718     return id;
719 }
720 
721 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name
722    Note: Second alternative
723    <template-prefix> <template-args>
724    in
725    <nested-name>
726    is redundant as it is included in <prefix> and is skipped here.
727  */
728 R decodeCxxNestedName(R)(Demangler!R x)
729 if (isInputRange!R)
730 {
731     if (x.show) dbg("rest: ", x.r);
732     if (x.r.skipOver('N')) // nested name: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name
733     {
734         const cvQ = x.r.decodeCxxCVQualifiers();
735         const refQ = x.decodeCxxRefQualifier();
736         const prefix = x.decodeCxxPrefix();
737         const name = x.decodeCxxUnqualifiedName();
738         assert(x.r.skipOver('E'));
739         auto ret = (cvQ.to!R ~
740                     prefix ~
741                     name ~
742                     refQ.toCxxString);
743         return ret;
744     }
745     return null;
746 }
747 
748 /** TODO Use this
749     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name
750  */
751 enum CtorDtorName
752 {
753     completeObjectConstructor,
754     baseObjectConstructor,
755     completeObjectAllocatingConstructor,
756     deletingDestructor,
757     completeObjectDestructor,
758     baseObjectDestructor
759 }
760 
761 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name */
762 R decodeCxxCtorDtorName(R)(Demangler!R x)
763 if (isInputRange!R)
764 {
765     if (x.show) dbg("rest: ", x.r);
766     R name;
767     enum n = 2;
768     if (x.r.length < n) { return typeof(return).init; }
769     import std.array: back;
770     switch (x.r[0..n])
771     {
772         case `C1`: name = x.sourceNames.back; break; // complete object constructor
773         case `C2`: name = x.sourceNames.back; break; // base object constructor
774         case `C3`: name = x.sourceNames.back; break; // complete object allocating constructor
775         case `D0`: name = '~' ~ x.sourceNames.back; break; // deleting destructor
776         case `D1`: name = '~' ~ x.sourceNames.back; break; // complete object destructor
777         case `D2`: name = '~' ~ x.sourceNames.back; break; // base object destructor
778         default: break;
779     }
780     if (name)
781     {
782         x.r.popFrontExactly(n);
783     }
784     return name;
785 }
786 
787 /** https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unqualified-name */
788 R decodeCxxUnqualifiedName(R)(Demangler!R x)
789 if (isInputRange!R)
790 {
791     if (x.show) dbg("rest: ", x.r);
792     return either(x.decodeCxxOperatorName(),
793                   x.decodeCxxSourceName(),
794                   x.decodeCxxCtorDtorName(),
795                   x.decodeCxxUnnamedTypeName());
796 }
797 
798 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unnamed-type-name */
799 R decodeCxxUnnamedTypeName(R)(Demangler!R x)
800 if (isInputRange!R)
801 {
802     if (x.show) dbg("rest: ", x.r);
803     R type;
804     if (x.r.skipOver(`Ut`))
805     {
806         type = x.decodeCxxNumber();
807         assert(x.r.skipOver('_'));
808     }
809     return type;
810 }
811 
812 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-prefix
813  */
814 R decodeCxxTemplatePrefix(R)(Demangler!R x)
815 if (isInputRange!R)
816 {
817     if (x.show) dbg("rest: ", x.r);
818     // NOTE: Removed <prefix> because of recursion
819     return either(x.decodeCxxUnqualifiedName(),
820                   x.decodeCxxTemplateParam(),
821                   x.decodeCxxSubstitution());
822 }
823 
824 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-args */
825 R[] decodeCxxTemplateArgs(R)(Demangler!R x)
826 if (isInputRange!R)
827 {
828     if (x.show) dbg("rest: ", x.r);
829     typeof(return) args;
830     if (x.r.skipOver('I'))
831     {
832         args ~= x.decodeCxxTemplateArg();
833         while (!x.r.empty)
834         {
835             auto arg = x.decodeCxxTemplateArg();
836             if (arg)
837             {
838                 args ~= arg;
839             }
840             else
841             {
842                 break;
843             }
844         }
845         assert(x.r.skipOver('E'));
846     }
847     return args;
848 }
849 
850 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name */
851 R decodeCxxMangledName(R)(Demangler!R x)
852 if (isInputRange!R)
853 {
854     if (x.show) dbg("rest: ", x.r);
855     R name;
856     if (x.r.skipOver(`_Z`))
857     {
858         return x.decodeCxxEncoding();
859     }
860     return name;
861 }
862 
863 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.expr-primary */
864 R decodeCxxExprPrimary(R)(Demangler!R x)
865 if (isInputRange!R)
866 {
867     if (x.show) dbg("rest: ", x.r);
868     R expr;
869     if (x.r.skipOver('L'))
870     {
871         expr = x.decodeCxxMangledName();
872         if (!expr)
873         {
874             auto number = x.decodeCxxNumber();
875             // TODO Howto demangle <float>?
876             // TODO Howto demangle <float> _ <float> E
877             expr = x.decodeCxxType(); // <R>, <nullptr>, <pointer> type
878             bool pointerType = x.r.skipOver('0'); // null pointer template argument
879         }
880         assert(x.r.skipOver('E'));
881     }
882     return expr;
883 }
884 
885 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-arg */
886 R decodeCxxTemplateArg(R)(Demangler!R x)
887 if (isInputRange!R)
888 {
889     if (x.show) dbg("rest: ", x.r);
890     R arg;
891     if (x.r.skipOver('X'))
892     {
893         arg = x.decodeCxxExpression();
894         assert(x.r.skipOver('E'));
895     }
896     else if (x.r.skipOver('J'))
897     {
898         R[] args;
899         while (!x.r.empty)
900         {
901             const subArg = x.decodeCxxTemplateArg();
902             if (subArg)
903             {
904                 args ~= subArg;
905             }
906             else
907             {
908                 break;
909             }
910         }
911         arg = args.joiner(`, `).to!R;
912         assert(x.r.skipOver('E'));
913     }
914     else
915     {
916         arg = either(x.decodeCxxExprPrimary(),
917                      x.decodeCxxType());
918     }
919     return arg;
920 }
921 
922 R decodeCxxTemplatePrefixAndArgs(R)(Demangler!R x)
923 if (isInputRange!R)
924 {
925     if (x.show) dbg("rest: ", x.r);
926     auto restBackup = x.r;
927     if (const prefix = x.decodeCxxTemplatePrefix())
928     {
929         auto args = x.decodeCxxTemplateArgs();
930         if (args)
931         {
932             return prefix ~ args.joiner(`, `).to!R;
933         }
934     }
935     x.r = restBackup; // restore upon failure
936     return typeof(return).init;
937 }
938 
939 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.prefix */
940 R decodeCxxPrefix(R)(Demangler!R x)
941 if (isInputRange!R)
942 {
943     if (x.show) dbg("rest: ", x.r);
944     typeof(return) prefix;
945     for (size_t i = 0; !x.r.empty; ++i) // NOTE: Turned self-recursion into iteration
946     {
947         if (const name = x.decodeCxxUnqualifiedName())
948         {
949             if (i >= 1)
950             {
951                 prefix ~= x.scopeSeparator;
952             }
953             prefix ~= name;
954             continue;
955         }
956         else if (const name = x.decodeCxxTemplatePrefixAndArgs())
957         {
958             prefix ~= name;
959             continue;
960         }
961         else if (const templateParam = x.decodeCxxTemplateParam())
962         {
963             prefix ~= templateParam;
964             continue;
965         }
966         else if (const decltype = x.decodeCxxDecltype())
967         {
968             prefix ~= decltype;
969             continue;
970         }
971         else if (const subst = x.decodeCxxSubstitution())
972         {
973             prefix ~= subst;
974             continue;
975         }
976         else
977         {
978             break;
979         }
980     }
981     return prefix;
982 }
983 
984 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-name */
985 R decodeCxxUnscopedName(R)(Demangler!R x)
986 if (isInputRange!R)
987 {
988     if (x.show) dbg("rest: ", x.r);
989     auto restBackup = x.r;
990     const prefix = x.r.skipOver(`St`) ? "::std::" : null;
991     if (const name = x.decodeCxxUnqualifiedName())
992     {
993         return prefix ~ name;
994     }
995     else
996     {
997         x.r = restBackup; // restore
998         return typeof(return).init;
999     }
1000 }
1001 
1002 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */
1003 R decodeCxxUnscopedTemplateName(R)(Demangler!R x)
1004 if (isInputRange!R)
1005 {
1006     if (x.show) dbg("rest: ", x.r);
1007     return either(x.decodeCxxSubstitution(), // faster backtracking with substitution
1008                   x.decodeCxxUnscopedName());
1009 }
1010 
1011 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */
1012 R decodeCxxUnscopedTemplateNameAndArgs(R)(Demangler!R x)
1013 if (isInputRange!R)
1014 {
1015     if (x.show) dbg("rest: ", x.r);
1016     R nameAndArgs;
1017     if (const name = x.decodeCxxUnscopedTemplateName())
1018     {
1019         nameAndArgs = name;
1020         if (auto args = x.decodeCxxTemplateArgs())
1021         {
1022             nameAndArgs ~= args.joiner(`, `).to!R;
1023         }
1024     }
1025     return nameAndArgs;
1026 }
1027 
1028 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.number */
1029 R decodeCxxNumber(R)(Demangler!R x)
1030 if (isInputRange!R)
1031 {
1032     if (x.show) dbg("rest: ", x.r);
1033     R number;
1034     const prefix = x.r.skipOver('n'); // optional prefix
1035     auto split = x.r.splitBefore!(a => !a.isDigit());
1036     if (prefix || !split[0].empty) // if complete match
1037     {
1038         x.r = split[1];
1039         number = split[0];
1040     }
1041     return number;
1042 }
1043 
1044 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.discriminator */
1045 R decodeCxxDescriminator(R)(Demangler!R x)
1046 if (isInputRange!R)
1047 {
1048     if (x.show) dbg("rest: ", x.r);
1049     R descriminator;
1050     if (x.r.skipOver('_'))
1051     {
1052         if (x.r.skipOver('_'))            // number >= 10
1053         {
1054             descriminator = x.decodeCxxNumber();
1055             assert(x.r.skipOver('_')); // suffix
1056         }
1057         else                    // number < 10
1058         {
1059             x.r.skipOver('n'); // optional prefix
1060             /* TODO Merge these two into a variant of popFront() that returns
1061              the popped element. What is best out of:
1062              - General: x.r.takeOne().to!R
1063              - Arrays only: r[0..1]
1064              - Needs cast: x.r.front
1065              and are we in need of a combined variant of front() and popFront()
1066              say takeFront() that may fail and requires a cast.
1067              */
1068             /* descriminator = r[0..1]; // single digit */
1069             /* x.r.popFront(); */
1070             descriminator = x.r.moveFront().to!R;
1071         }
1072     }
1073     return descriminator;
1074 }
1075 
1076 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.local-name */
1077 R decodeCxxLocalName(R)(Demangler!R x)
1078 if (isInputRange!R)
1079 {
1080     if (x.show) dbg("rest: ", x.r);
1081     if (x.r.skipOver('Z'))
1082     {
1083         const functionEncoding = x.decodeCxxEncoding();
1084         x.r.skipOver('E');
1085         if (x.r.skipOver('D'))
1086         {
1087             assert(0, "TODO Decode C++0x Closure Type (lambda)"); // see https://mentorembedded.github.io/cxx-abi/abi.html#closure-types
1088         }
1089         else
1090         {
1091             const entityNameMaybe = either(x.skipLiteral('s'), // NOTE: Literal first to speed up
1092                                            x.decodeCxxName());
1093             const discriminator = x.decodeCxxDescriminator(); // optional
1094             return (functionEncoding ~
1095                     x.scopeSeparator ~
1096                     entityNameMaybe ~
1097                     discriminator.to!R); // TODO Optional
1098         }
1099     }
1100     return R.init;
1101 }
1102 
1103 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.name */
1104 R decodeCxxName(R)(Demangler!R x)
1105 if (isInputRange!R)
1106 {
1107     if (x.show) dbg("rest: ", x.r);
1108     return either(x.decodeCxxNestedName(),
1109                   x.decodeCxxUnscopedName(),
1110                   x.decodeCxxLocalName(), // TODO order flipped
1111                   x.decodeCxxUnscopedTemplateNameAndArgs()); // NOTE: order flipped
1112 }
1113 
1114 R decodeCxxNVOffset(R)(Demangler!R x)
1115 if (isInputRange!R)
1116 {
1117     if (x.show) dbg("rest: ", x.r);
1118     return x.decodeCxxNumber();
1119 }
1120 
1121 R decodeCxxVOffset(R)(Demangler!R x)
1122 if (isInputRange!R)
1123 {
1124     if (x.show) dbg("rest: ", x.r);
1125     auto offset = x.decodeCxxNumber();
1126     assert(x.r.skipOver('_'));
1127     return offset ~ x.decodeCxxNumber();
1128 }
1129 
1130 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.call-offset */
1131 R decodeCxxCallOffset(R)(Demangler!R x)
1132 if (isInputRange!R)
1133 {
1134     if (x.show) dbg("rest: ", x.r);
1135     typeof(return) offset;
1136     if (x.r.skipOver('h'))
1137     {
1138         offset = x.decodeCxxNVOffset();
1139         assert(x.r.skipOver('_'));
1140     }
1141     else if (x.r.skipOver('v'))
1142     {
1143         offset = x.decodeCxxVOffset();
1144         assert(x.r.skipOver('_'));
1145     }
1146     return offset;
1147 }
1148 
1149 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.special-name */
1150 R decodeCxxSpecialName(R)(Demangler!R x)
1151 if (isInputRange!R)
1152 {
1153     if (x.show) dbg("rest: ", x.r);
1154     auto restBackup = x.r;
1155     typeof(return) name;
1156     if (x.r.skipOver('S'))
1157     {
1158         switch (x.r.moveFront)
1159         {
1160             case 'V': name = "virtual table: "; break;
1161             case 'T': name = "VTT structure: "; break;
1162             case 'I': name = "typeinfo structure: "; break;
1163             case 'S': name = "typeinfo name (null-terminated byte R): "; break;
1164             default:
1165                 x.r = restBackup; // restore
1166                 return name;
1167         }
1168         name ~= x.decodeCxxType();
1169     }
1170     else if (x.r.skipOver(`GV`))
1171     {
1172         name = x.decodeCxxName();
1173     }
1174     else if (x.r.skipOver('T'))
1175     {
1176         if (x.r.skipOver('c'))
1177         {
1178             name = x.r.tryEvery(x.decodeCxxCallOffset(),
1179                                 x.decodeCxxCallOffset(),
1180                                 x.decodeCxxEncoding()).joiner(` `).to!R;
1181         }
1182         else
1183         {
1184             name = x.r.tryEvery(x.decodeCxxCallOffset(),
1185                                 x.decodeCxxEncoding()).joiner(` `).to!R;
1186         }
1187     }
1188     return name;
1189 }
1190 
1191 /* Decode C++ Symbol.
1192    See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.encoding
1193  */
1194 R decodeCxxEncoding(R)(Demangler!R x) /* @safe pure nothrow @nogc */ if (isInputRange!R)
1195 {
1196 if (x.show) dbg("rest: ", x.r);
1197     const localFlag = x.r.skipOver('L'); // TODO What role does the L have in symbols starting with _ZL have?
1198     if (const name = x.decodeCxxSpecialName())
1199     {
1200         return name;
1201     }
1202     else
1203     {
1204         const name = x.decodeCxxName();
1205         auto type = x.decodeCxxBareFunctionType();
1206         return name ~ type.to!R;
1207     }
1208 }
1209 
1210 /** Demangled Expression. */
1211 alias Expr = string;
1212 
1213 struct Demangling
1214 {
1215     Lang language;
1216     Expr unmangled;
1217     auto opCast(T : bool)()
1218         @safe pure nothrow const
1219     {
1220         return !expr.empty;
1221     }
1222 }
1223 
1224 /** Demangle Symbol $(D r) and Detect Language.
1225     See_Also: https://en.wikipedia.org/wiki/Name_mangling
1226     See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling
1227     See_Also: https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
1228 */
1229 Demangling decodeSymbol(R)(Demangler!R x) /* @safe pure nothrow @nogc */
1230 if (isInputRange!R)
1231 {
1232     if (x.r.empty)
1233     {
1234         return Demangling(Lang.init, x.r);
1235     }
1236 
1237     if (!x.r.startsWith('_'))
1238     {
1239         return Demangling(Lang.c, x.r); // assume C
1240     }
1241 
1242     // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name
1243     if (x.r.skipOver(`_Z`))
1244     {
1245         return Demangling(Lang.cxx,
1246                           x.decodeCxxEncoding());
1247     }
1248     else
1249     {
1250         import core.demangle: demangle;
1251         const symAsD = x.r.demangle;
1252         import std.conv: to;
1253         if (symAsD != x.r) // TODO Why doesn't (symAsD is r) work here?
1254             return Demangling(Lang.d, symAsD.to!R);
1255         else
1256             return Demangling(Lang.init, x.r);
1257     }
1258 }
1259 
1260 unittest
1261 {
1262     import nxt.assert_ex;
1263 
1264     assertEqual(demangler(`memcpy`).decodeSymbol(),
1265                 Demangling(Lang.c, `memcpy`));
1266 
1267     assertEqual(demangler(`memcpy`).decodeSymbol(),
1268                 Demangling(Lang.c, `memcpy`));
1269 
1270     assertEqual(demangler(`_Z1hi`).decodeSymbol(),
1271                 Demangling(Lang.cxx, `h(int)`));
1272 
1273     assertEqual(demangler(`_Z3foo3bar`).decodeSymbol(),
1274                 Demangling(Lang.cxx, `foo(bar)`));
1275 
1276     assertEqual(demangler(`_ZN1N1fE`).decodeSymbol(),
1277                 Demangling(Lang.cxx, `N::f`));
1278 
1279     assertEqual(demangler(`_ZN3Foo3BarEv`).decodeSymbol(),
1280                 Demangling(Lang.cxx, `Foo::Bar()`));
1281 
1282     assertEqual(demangler(`_ZN3FooC1Ev`).decodeSymbol(),
1283                 Demangling(Lang.cxx, `Foo::Foo()`));
1284 
1285     assertEqual(demangler(`_ZN9wikipedia7article6formatE`).decodeSymbol(),
1286                 Demangling(Lang.cxx, `wikipedia::article::format`));
1287 
1288     assertEqual(demangler(`_ZSt5state`).decodeSymbol(),
1289                 Demangling(Lang.cxx, `::std::state`));
1290 
1291     assertEqual(demangler(`_ZN9wikipedia7article8print_toERSo`).decodeSymbol(),
1292                 Demangling(Lang.cxx, `wikipedia::article::print_to(::std::ostream&)`));
1293 
1294     assertEqual(demangler(`_ZN9wikipedia7article8print_toEOSo`).decodeSymbol(),
1295                 Demangling(Lang.cxx, `wikipedia::article::print_to(::std::ostream&&)`));
1296 
1297     assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, true).decodeSymbol(),
1298                 Demangling(Lang.cxx, `wikipedia::article::format(void)`));
1299 
1300     assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, false).decodeSymbol(),
1301                 Demangling(Lang.cxx, `wikipedia::article::format()`));
1302 
1303     assertEqual(demangler(`_ZL8next_argRPPc`).decodeSymbol(),
1304                 Demangling(Lang.cxx, `next_arg(char**&)`));
1305 
1306     assertEqual(demangler(`_ZL10parse_archmPPKcS0_`, true).decodeSymbol(),
1307                 Demangling(Lang.cxx, `parse_arch(unsigned long, char const**, char const*)`));
1308 
1309     assertEqual(demangler(`_ZN5LexerC2EP6ModulePKhmmii`, true).decodeSymbol(),
1310                 Demangling(Lang.cxx, `Lexer::Lexer(Module*, unsigned char const*, unsigned long, unsigned long, int, int)`));
1311 
1312     assertEqual(demangler(`_Zrm1XS_`).decodeSymbol(),
1313                 Demangling(Lang.cxx, `operator%(X, X)`));
1314 
1315     assertEqual(demangler(`_ZZL8next_argRPPcE4keys`).decodeSymbol(),
1316                 Demangling(Lang.cxx, `next_arg(char**&)::keys`));
1317 
1318     assertEqual(demangler(`_ZN12ExpStatement9scopeCodeEP5ScopePP9StatementS4_S4`).decodeSymbol(),
1319                 Demangling(Lang.cxx, `ExpStatement::scopeCode(Scope*, Statement**, Statement**, Statement**)`));
1320 
1321     assertEqual(demangler(`_ZZ8genCmainP5ScopeE9cmaincode`).decodeSymbol(),
1322                 Demangling(Lang.cxx, `genCmain(Scope*)::cmaincode`));
1323 
1324     assertEqual(demangler("_Z7DtoLValP6DValue").decodeSymbol(),
1325                 Demangling(Lang.cxx, `DtoLVal(DValue*)`));
1326 }