]>
Commit | Line | Data |
---|---|---|
b4c522fa IB |
1 | /** |
2 | * The demangle module converts mangled D symbols to a representation similar | |
3 | * to what would have existed in code. | |
4 | * | |
5 | * Copyright: Copyright Sean Kelly 2010 - 2014. | |
6 | * License: Distributed under the | |
7 | * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). | |
8 | * (See accompanying file LICENSE) | |
9 | * Authors: Sean Kelly | |
10 | * Source: $(DRUNTIMESRC core/_demangle.d) | |
11 | */ | |
12 | ||
13 | module core.demangle; | |
14 | ||
15 | version (OSX) | |
16 | version = Darwin; | |
17 | else version (iOS) | |
18 | version = Darwin; | |
19 | else version (TVOS) | |
20 | version = Darwin; | |
21 | else version (WatchOS) | |
22 | version = Darwin; | |
23 | ||
24 | debug(trace) import core.stdc.stdio : printf; | |
25 | debug(info) import core.stdc.stdio : printf; | |
26 | ||
27 | private struct NoHooks | |
28 | { | |
29 | // supported hooks | |
30 | // static bool parseLName(ref Demangle); | |
31 | // static char[] parseType(ref Demangle, char[]) | |
32 | } | |
33 | ||
34 | private struct Demangle(Hooks = NoHooks) | |
35 | { | |
36 | // NOTE: This implementation currently only works with mangled function | |
37 | // names as they exist in an object file. Type names mangled via | |
38 | // the .mangleof property are effectively incomplete as far as the | |
39 | // ABI is concerned and so are not considered to be mangled symbol | |
40 | // names. | |
41 | ||
42 | // NOTE: This implementation builds the demangled buffer in place by | |
43 | // writing data as it is decoded and then rearranging it later as | |
44 | // needed. In practice this results in very little data movement, | |
45 | // and the performance cost is more than offset by the gain from | |
46 | // not allocating dynamic memory to assemble the name piecemeal. | |
47 | // | |
48 | // If the destination buffer is too small, parsing will restart | |
49 | // with a larger buffer. Since this generally means only one | |
50 | // allocation during the course of a parsing run, this is still | |
51 | // faster than assembling the result piecemeal. | |
52 | ||
53 | pure @safe: | |
54 | enum AddType { no, yes } | |
55 | ||
56 | ||
5fee5ec3 | 57 | this( return const(char)[] buf_, return char[] dst_ = null ) |
b4c522fa IB |
58 | { |
59 | this( buf_, AddType.yes, dst_ ); | |
60 | } | |
61 | ||
62 | ||
5fee5ec3 | 63 | this( return const(char)[] buf_, AddType addType_, return char[] dst_ = null ) |
b4c522fa IB |
64 | { |
65 | buf = buf_; | |
66 | addType = addType_; | |
67 | dst = dst_; | |
68 | } | |
69 | ||
70 | ||
71 | enum size_t minBufSize = 4000; | |
72 | ||
73 | ||
74 | const(char)[] buf = null; | |
75 | char[] dst = null; | |
76 | size_t pos = 0; | |
77 | size_t len = 0; | |
78 | size_t brp = 0; // current back reference pos | |
79 | AddType addType = AddType.yes; | |
80 | bool mute = false; | |
81 | Hooks hooks; | |
82 | ||
83 | static class ParseException : Exception | |
84 | { | |
85 | @safe pure nothrow this( string msg ) | |
86 | { | |
87 | super( msg ); | |
88 | } | |
89 | } | |
90 | ||
91 | ||
92 | static class OverflowException : Exception | |
93 | { | |
94 | @safe pure nothrow this( string msg ) | |
95 | { | |
96 | super( msg ); | |
97 | } | |
98 | } | |
99 | ||
100 | ||
101 | static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ | |
102 | { | |
103 | pragma(inline, false); // tame dmd inliner | |
104 | ||
105 | //throw new ParseException( msg ); | |
106 | debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); | |
107 | throw __ctfe ? new ParseException(msg) | |
108 | : cast(ParseException) cast(void*) typeid(ParseException).initializer; | |
109 | ||
110 | } | |
111 | ||
112 | ||
113 | static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ | |
114 | { | |
115 | pragma(inline, false); // tame dmd inliner | |
116 | ||
117 | //throw new OverflowException( msg ); | |
118 | debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); | |
119 | throw cast(OverflowException) cast(void*) typeid(OverflowException).initializer; | |
120 | } | |
121 | ||
122 | ||
123 | ////////////////////////////////////////////////////////////////////////// | |
124 | // Type Testing and Conversion | |
125 | ////////////////////////////////////////////////////////////////////////// | |
126 | ||
127 | ||
128 | static bool isAlpha( char val ) | |
129 | { | |
130 | return ('a' <= val && 'z' >= val) || | |
131 | ('A' <= val && 'Z' >= val) || | |
132 | (0x80 & val); // treat all unicode as alphabetic | |
133 | } | |
134 | ||
135 | ||
136 | static bool isDigit( char val ) | |
137 | { | |
138 | return '0' <= val && '9' >= val; | |
139 | } | |
140 | ||
141 | ||
142 | static bool isHexDigit( char val ) | |
143 | { | |
144 | return ('0' <= val && '9' >= val) || | |
145 | ('a' <= val && 'f' >= val) || | |
146 | ('A' <= val && 'F' >= val); | |
147 | } | |
148 | ||
149 | ||
150 | static ubyte ascii2hex( char val ) | |
151 | { | |
152 | if (val >= 'a' && val <= 'f') | |
153 | return cast(ubyte)(val - 'a' + 10); | |
154 | if (val >= 'A' && val <= 'F') | |
155 | return cast(ubyte)(val - 'A' + 10); | |
156 | if (val >= '0' && val <= '9') | |
157 | return cast(ubyte)(val - '0'); | |
158 | error(); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | ||
163 | ////////////////////////////////////////////////////////////////////////// | |
164 | // Data Output | |
165 | ////////////////////////////////////////////////////////////////////////// | |
166 | ||
167 | ||
168 | static bool contains( const(char)[] a, const(char)[] b ) @trusted | |
169 | { | |
170 | if (a.length && b.length) | |
171 | { | |
172 | auto bend = b.ptr + b.length; | |
173 | auto aend = a.ptr + a.length; | |
174 | return a.ptr <= b.ptr && bend <= aend; | |
175 | } | |
176 | return false; | |
177 | } | |
178 | ||
179 | ||
180 | // move val to the end of the dst buffer | |
181 | char[] shift( const(char)[] val ) | |
182 | { | |
183 | pragma(inline, false); // tame dmd inliner | |
184 | ||
185 | if ( val.length && !mute ) | |
186 | { | |
187 | assert( contains( dst[0 .. len], val ) ); | |
188 | debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); | |
189 | ||
190 | if (len + val.length > dst.length) | |
191 | overflow(); | |
192 | size_t v = &val[0] - &dst[0]; | |
193 | dst[len .. len + val.length] = val[]; | |
194 | for (size_t p = v; p < len; p++) | |
195 | dst[p] = dst[p + val.length]; | |
196 | ||
197 | return dst[len - val.length .. len]; | |
198 | } | |
199 | return null; | |
200 | } | |
201 | ||
202 | // remove val from dst buffer | |
203 | void remove( const(char)[] val ) | |
204 | { | |
205 | pragma(inline, false); // tame dmd inliner | |
206 | ||
207 | if ( val.length ) | |
208 | { | |
209 | assert( contains( dst[0 .. len], val ) ); | |
210 | debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); | |
b4c522fa | 211 | size_t v = &val[0] - &dst[0]; |
5fee5ec3 IB |
212 | assert( len >= val.length && len <= dst.length ); |
213 | len -= val.length; | |
b4c522fa IB |
214 | for (size_t p = v; p < len; p++) |
215 | dst[p] = dst[p + val.length]; | |
b4c522fa IB |
216 | } |
217 | } | |
218 | ||
5fee5ec3 | 219 | char[] append( const(char)[] val ) return scope |
b4c522fa IB |
220 | { |
221 | pragma(inline, false); // tame dmd inliner | |
222 | ||
223 | if ( val.length && !mute ) | |
224 | { | |
225 | if ( !dst.length ) | |
226 | dst.length = minBufSize; | |
227 | assert( !contains( dst[0 .. len], val ) ); | |
228 | debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); | |
229 | ||
5fee5ec3 | 230 | if ( dst.length - len >= val.length && &dst[len] == &val[0] ) |
b4c522fa IB |
231 | { |
232 | // data is already in place | |
233 | auto t = dst[len .. len + val.length]; | |
234 | len += val.length; | |
235 | return t; | |
236 | } | |
237 | if ( dst.length - len >= val.length ) | |
238 | { | |
239 | dst[len .. len + val.length] = val[]; | |
240 | auto t = dst[len .. len + val.length]; | |
241 | len += val.length; | |
242 | return t; | |
243 | } | |
244 | overflow(); | |
245 | } | |
246 | return null; | |
247 | } | |
248 | ||
249 | void putComma(size_t n) | |
250 | { | |
251 | pragma(inline, false); | |
252 | if (n) | |
253 | put(", "); | |
254 | } | |
255 | ||
5fee5ec3 | 256 | char[] put(char c) return scope |
b4c522fa IB |
257 | { |
258 | char[1] val = c; | |
259 | return put(val[]); | |
260 | } | |
261 | ||
5fee5ec3 | 262 | char[] put( scope const(char)[] val ) return scope |
b4c522fa IB |
263 | { |
264 | pragma(inline, false); // tame dmd inliner | |
265 | ||
266 | if ( val.length ) | |
267 | { | |
268 | if ( !contains( dst[0 .. len], val ) ) | |
269 | return append( val ); | |
270 | return shift( val ); | |
271 | } | |
272 | return null; | |
273 | } | |
274 | ||
275 | ||
276 | void putAsHex( size_t val, int width = 0 ) | |
277 | { | |
278 | import core.internal.string; | |
279 | ||
5fee5ec3 | 280 | UnsignedStringBuf buf = void; |
b4c522fa | 281 | |
5fee5ec3 | 282 | auto s = unsignedToTempString!16(val, buf); |
b4c522fa IB |
283 | int slen = cast(int)s.length; |
284 | if (slen < width) | |
285 | { | |
286 | foreach (i; slen .. width) | |
287 | put('0'); | |
288 | } | |
289 | put(s); | |
290 | } | |
291 | ||
292 | ||
293 | void pad( const(char)[] val ) | |
294 | { | |
295 | if ( val.length ) | |
296 | { | |
297 | append( " " ); | |
298 | put( val ); | |
299 | } | |
300 | } | |
301 | ||
302 | ||
5fee5ec3 | 303 | void silent( void delegate() pure @safe dg ) |
b4c522fa IB |
304 | { |
305 | debug(trace) printf( "silent+\n" ); | |
306 | debug(trace) scope(success) printf( "silent-\n" ); | |
307 | auto n = len; dg(); len = n; | |
308 | } | |
309 | ||
310 | ||
311 | ////////////////////////////////////////////////////////////////////////// | |
312 | // Parsing Utility | |
313 | ////////////////////////////////////////////////////////////////////////// | |
314 | ||
315 | @property bool empty() | |
316 | { | |
317 | return pos >= buf.length; | |
318 | } | |
319 | ||
320 | @property char front() | |
321 | { | |
322 | if ( pos < buf.length ) | |
323 | return buf[pos]; | |
324 | return char.init; | |
325 | } | |
326 | ||
327 | char peek( size_t n ) | |
328 | { | |
329 | if ( pos + n < buf.length ) | |
330 | return buf[pos + n]; | |
331 | return char.init; | |
332 | } | |
333 | ||
334 | ||
335 | void test( char val ) | |
336 | { | |
337 | if ( val != front ) | |
338 | error(); | |
339 | } | |
340 | ||
341 | ||
342 | void popFront() | |
343 | { | |
344 | if ( pos++ >= buf.length ) | |
345 | error(); | |
346 | } | |
347 | ||
348 | ||
349 | void match( char val ) | |
350 | { | |
351 | test( val ); | |
352 | popFront(); | |
353 | } | |
354 | ||
355 | ||
356 | void match( const(char)[] val ) | |
357 | { | |
358 | foreach (char e; val ) | |
359 | { | |
360 | test( e ); | |
361 | popFront(); | |
362 | } | |
363 | } | |
364 | ||
365 | ||
366 | void eat( char val ) | |
367 | { | |
368 | if ( val == front ) | |
369 | popFront(); | |
370 | } | |
371 | ||
372 | bool isSymbolNameFront() | |
373 | { | |
374 | char val = front; | |
375 | if ( isDigit( val ) || val == '_' ) | |
376 | return true; | |
377 | if ( val != 'Q' ) | |
378 | return false; | |
379 | ||
380 | // check the back reference encoding after 'Q' | |
381 | val = peekBackref(); | |
382 | return isDigit( val ); // identifier ref | |
383 | } | |
384 | ||
385 | // return the first character at the back reference | |
386 | char peekBackref() | |
387 | { | |
388 | assert( front == 'Q' ); | |
389 | auto n = decodeBackref!1(); | |
390 | if (!n || n > pos) | |
391 | error("invalid back reference"); | |
392 | ||
393 | return buf[pos - n]; | |
394 | } | |
395 | ||
396 | size_t decodeBackref(size_t peekAt = 0)() | |
397 | { | |
398 | enum base = 26; | |
399 | size_t n = 0; | |
400 | for (size_t p; ; p++) | |
401 | { | |
402 | char t; | |
403 | static if (peekAt > 0) | |
404 | { | |
405 | t = peek(peekAt + p); | |
406 | } | |
407 | else | |
408 | { | |
409 | t = front; | |
410 | popFront(); | |
411 | } | |
412 | if (t < 'A' || t > 'Z') | |
413 | { | |
414 | if (t < 'a' || t > 'z') | |
415 | error("invalid back reference"); | |
416 | n = base * n + t - 'a'; | |
417 | return n; | |
418 | } | |
419 | n = base * n + t - 'A'; | |
420 | } | |
421 | } | |
422 | ||
423 | ////////////////////////////////////////////////////////////////////////// | |
424 | // Parsing Implementation | |
425 | ////////////////////////////////////////////////////////////////////////// | |
426 | ||
427 | ||
428 | /* | |
429 | Number: | |
430 | Digit | |
431 | Digit Number | |
432 | */ | |
5fee5ec3 | 433 | const(char)[] sliceNumber() return scope |
b4c522fa IB |
434 | { |
435 | debug(trace) printf( "sliceNumber+\n" ); | |
436 | debug(trace) scope(success) printf( "sliceNumber-\n" ); | |
437 | ||
438 | auto beg = pos; | |
439 | ||
440 | while ( true ) | |
441 | { | |
442 | auto t = front; | |
443 | if (t >= '0' && t <= '9') | |
444 | popFront(); | |
445 | else | |
446 | return buf[beg .. pos]; | |
447 | } | |
448 | } | |
449 | ||
450 | ||
5fee5ec3 | 451 | size_t decodeNumber() scope |
b4c522fa IB |
452 | { |
453 | debug(trace) printf( "decodeNumber+\n" ); | |
454 | debug(trace) scope(success) printf( "decodeNumber-\n" ); | |
455 | ||
456 | return decodeNumber( sliceNumber() ); | |
457 | } | |
458 | ||
459 | ||
5fee5ec3 | 460 | size_t decodeNumber( scope const(char)[] num ) scope |
b4c522fa IB |
461 | { |
462 | debug(trace) printf( "decodeNumber+\n" ); | |
463 | debug(trace) scope(success) printf( "decodeNumber-\n" ); | |
464 | ||
465 | size_t val = 0; | |
466 | ||
467 | foreach ( c; num ) | |
468 | { | |
469 | import core.checkedint : mulu, addu; | |
470 | ||
471 | bool overflow = false; | |
472 | val = mulu(val, 10, overflow); | |
473 | val = addu(val, c - '0', overflow); | |
474 | if (overflow) | |
475 | error(); | |
476 | } | |
477 | return val; | |
478 | } | |
479 | ||
480 | ||
5fee5ec3 | 481 | void parseReal() scope |
b4c522fa IB |
482 | { |
483 | debug(trace) printf( "parseReal+\n" ); | |
484 | debug(trace) scope(success) printf( "parseReal-\n" ); | |
485 | ||
486 | char[64] tbuf = void; | |
487 | size_t tlen = 0; | |
488 | real val = void; | |
489 | ||
490 | if ( 'I' == front ) | |
491 | { | |
492 | match( "INF" ); | |
493 | put( "real.infinity" ); | |
494 | return; | |
495 | } | |
496 | if ( 'N' == front ) | |
497 | { | |
498 | popFront(); | |
499 | if ( 'I' == front ) | |
500 | { | |
501 | match( "INF" ); | |
502 | put( "-real.infinity" ); | |
503 | return; | |
504 | } | |
505 | if ( 'A' == front ) | |
506 | { | |
507 | match( "AN" ); | |
508 | put( "real.nan" ); | |
509 | return; | |
510 | } | |
511 | tbuf[tlen++] = '-'; | |
512 | } | |
513 | ||
514 | tbuf[tlen++] = '0'; | |
515 | tbuf[tlen++] = 'X'; | |
516 | if ( !isHexDigit( front ) ) | |
517 | error( "Expected hex digit" ); | |
518 | tbuf[tlen++] = front; | |
519 | tbuf[tlen++] = '.'; | |
520 | popFront(); | |
521 | ||
522 | while ( isHexDigit( front ) ) | |
523 | { | |
524 | tbuf[tlen++] = front; | |
525 | popFront(); | |
526 | } | |
527 | match( 'P' ); | |
528 | tbuf[tlen++] = 'p'; | |
529 | if ( 'N' == front ) | |
530 | { | |
531 | tbuf[tlen++] = '-'; | |
532 | popFront(); | |
533 | } | |
534 | else | |
535 | { | |
536 | tbuf[tlen++] = '+'; | |
537 | } | |
538 | while ( isDigit( front ) ) | |
539 | { | |
540 | tbuf[tlen++] = front; | |
541 | popFront(); | |
542 | } | |
543 | ||
544 | tbuf[tlen] = 0; | |
545 | debug(info) printf( "got (%s)\n", tbuf.ptr ); | |
546 | pureReprintReal( tbuf[] ); | |
547 | debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr ); | |
548 | put( tbuf[0 .. tlen] ); | |
549 | } | |
550 | ||
551 | ||
552 | /* | |
553 | LName: | |
554 | Number Name | |
555 | ||
556 | Name: | |
557 | Namestart | |
558 | Namestart Namechars | |
559 | ||
560 | Namestart: | |
561 | _ | |
562 | Alpha | |
563 | ||
564 | Namechar: | |
565 | Namestart | |
566 | Digit | |
567 | ||
568 | Namechars: | |
569 | Namechar | |
570 | Namechar Namechars | |
571 | */ | |
5fee5ec3 | 572 | void parseLName() scope |
b4c522fa IB |
573 | { |
574 | debug(trace) printf( "parseLName+\n" ); | |
575 | debug(trace) scope(success) printf( "parseLName-\n" ); | |
576 | ||
577 | static if (__traits(hasMember, Hooks, "parseLName")) | |
578 | if (hooks.parseLName(this)) | |
579 | return; | |
580 | ||
581 | if ( front == 'Q' ) | |
582 | { | |
583 | // back reference to LName | |
584 | auto refPos = pos; | |
585 | popFront(); | |
586 | size_t n = decodeBackref(); | |
587 | if ( !n || n > refPos ) | |
588 | error( "Invalid LName back reference" ); | |
589 | if ( !mute ) | |
590 | { | |
591 | auto savePos = pos; | |
592 | scope(exit) pos = savePos; | |
593 | pos = refPos - n; | |
594 | parseLName(); | |
595 | } | |
596 | return; | |
597 | } | |
598 | auto n = decodeNumber(); | |
599 | if ( n == 0 ) | |
600 | { | |
601 | put( "__anonymous" ); | |
602 | return; | |
603 | } | |
604 | if ( n > buf.length || n > buf.length - pos ) | |
605 | error( "LName must be at least 1 character" ); | |
606 | if ( '_' != front && !isAlpha( front ) ) | |
607 | error( "Invalid character in LName" ); | |
608 | foreach (char e; buf[pos + 1 .. pos + n] ) | |
609 | { | |
610 | if ( '_' != e && !isAlpha( e ) && !isDigit( e ) ) | |
611 | error( "Invalid character in LName" ); | |
612 | } | |
613 | ||
614 | put( buf[pos .. pos + n] ); | |
615 | pos += n; | |
616 | } | |
617 | ||
618 | ||
619 | /* | |
620 | Type: | |
621 | Shared | |
622 | Const | |
623 | Immutable | |
624 | Wild | |
625 | TypeArray | |
626 | TypeVector | |
627 | TypeStaticArray | |
628 | TypeAssocArray | |
629 | TypePointer | |
630 | TypeFunction | |
631 | TypeIdent | |
632 | TypeClass | |
633 | TypeStruct | |
634 | TypeEnum | |
635 | TypeTypedef | |
636 | TypeDelegate | |
637 | TypeNone | |
638 | TypeVoid | |
639 | TypeByte | |
640 | TypeUbyte | |
641 | TypeShort | |
642 | TypeUshort | |
643 | TypeInt | |
644 | TypeUint | |
645 | TypeLong | |
646 | TypeUlong | |
647 | TypeCent | |
648 | TypeUcent | |
649 | TypeFloat | |
650 | TypeDouble | |
651 | TypeReal | |
652 | TypeIfloat | |
653 | TypeIdouble | |
654 | TypeIreal | |
655 | TypeCfloat | |
656 | TypeCdouble | |
657 | TypeCreal | |
658 | TypeBool | |
659 | TypeChar | |
660 | TypeWchar | |
661 | TypeDchar | |
662 | TypeTuple | |
663 | ||
664 | Shared: | |
665 | O Type | |
666 | ||
667 | Const: | |
668 | x Type | |
669 | ||
670 | Immutable: | |
671 | y Type | |
672 | ||
673 | Wild: | |
674 | Ng Type | |
675 | ||
676 | TypeArray: | |
677 | A Type | |
678 | ||
679 | TypeVector: | |
680 | Nh Type | |
681 | ||
682 | TypeStaticArray: | |
683 | G Number Type | |
684 | ||
685 | TypeAssocArray: | |
686 | H Type Type | |
687 | ||
688 | TypePointer: | |
689 | P Type | |
690 | ||
691 | TypeFunction: | |
692 | CallConvention FuncAttrs Arguments ArgClose Type | |
693 | ||
694 | TypeIdent: | |
695 | I LName | |
696 | ||
697 | TypeClass: | |
698 | C LName | |
699 | ||
700 | TypeStruct: | |
701 | S LName | |
702 | ||
703 | TypeEnum: | |
704 | E LName | |
705 | ||
706 | TypeTypedef: | |
707 | T LName | |
708 | ||
709 | TypeDelegate: | |
710 | D TypeFunction | |
711 | ||
712 | TypeNone: | |
713 | n | |
714 | ||
715 | TypeVoid: | |
716 | v | |
717 | ||
718 | TypeByte: | |
719 | g | |
720 | ||
721 | TypeUbyte: | |
722 | h | |
723 | ||
724 | TypeShort: | |
725 | s | |
726 | ||
727 | TypeUshort: | |
728 | t | |
729 | ||
730 | TypeInt: | |
731 | i | |
732 | ||
733 | TypeUint: | |
734 | k | |
735 | ||
736 | TypeLong: | |
737 | l | |
738 | ||
739 | TypeUlong: | |
740 | m | |
741 | ||
742 | TypeCent | |
743 | zi | |
744 | ||
745 | TypeUcent | |
746 | zk | |
747 | ||
748 | TypeFloat: | |
749 | f | |
750 | ||
751 | TypeDouble: | |
752 | d | |
753 | ||
754 | TypeReal: | |
755 | e | |
756 | ||
757 | TypeIfloat: | |
758 | o | |
759 | ||
760 | TypeIdouble: | |
761 | p | |
762 | ||
763 | TypeIreal: | |
764 | j | |
765 | ||
766 | TypeCfloat: | |
767 | q | |
768 | ||
769 | TypeCdouble: | |
770 | r | |
771 | ||
772 | TypeCreal: | |
773 | c | |
774 | ||
775 | TypeBool: | |
776 | b | |
777 | ||
778 | TypeChar: | |
779 | a | |
780 | ||
781 | TypeWchar: | |
782 | u | |
783 | ||
784 | TypeDchar: | |
785 | w | |
786 | ||
787 | TypeTuple: | |
788 | B Number Arguments | |
789 | */ | |
5fee5ec3 | 790 | char[] parseType( char[] name = null ) return scope |
b4c522fa IB |
791 | { |
792 | static immutable string[23] primitives = [ | |
793 | "char", // a | |
794 | "bool", // b | |
795 | "creal", // c | |
796 | "double", // d | |
797 | "real", // e | |
798 | "float", // f | |
799 | "byte", // g | |
800 | "ubyte", // h | |
801 | "int", // i | |
802 | "ireal", // j | |
803 | "uint", // k | |
804 | "long", // l | |
805 | "ulong", // m | |
806 | null, // n | |
807 | "ifloat", // o | |
808 | "idouble", // p | |
809 | "cfloat", // q | |
810 | "cdouble", // r | |
811 | "short", // s | |
812 | "ushort", // t | |
813 | "wchar", // u | |
814 | "void", // v | |
815 | "dchar", // w | |
816 | ]; | |
817 | ||
818 | static if (__traits(hasMember, Hooks, "parseType")) | |
819 | if (auto n = hooks.parseType(this, name)) | |
820 | return n; | |
821 | ||
822 | debug(trace) printf( "parseType+\n" ); | |
823 | debug(trace) scope(success) printf( "parseType-\n" ); | |
824 | auto beg = len; | |
825 | auto t = front; | |
826 | ||
827 | char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe | |
828 | { | |
829 | if (pos == brp) | |
830 | error("recursive back reference"); | |
831 | auto refPos = pos; | |
832 | popFront(); | |
833 | auto n = decodeBackref(); | |
834 | if (n == 0 || n > pos) | |
835 | error("invalid back reference"); | |
836 | if ( mute ) | |
837 | return null; | |
838 | auto savePos = pos; | |
839 | auto saveBrp = brp; | |
840 | scope(success) { pos = savePos; brp = saveBrp; } | |
841 | pos = refPos - n; | |
842 | brp = refPos; | |
843 | auto ret = parseDg(); | |
844 | return ret; | |
845 | } | |
846 | ||
847 | switch ( t ) | |
848 | { | |
849 | case 'Q': // Type back reference | |
850 | return parseBackrefType( () => parseType( name ) ); | |
851 | case 'O': // Shared (O Type) | |
852 | popFront(); | |
853 | put( "shared(" ); | |
854 | parseType(); | |
855 | put( ')' ); | |
856 | pad( name ); | |
857 | return dst[beg .. len]; | |
858 | case 'x': // Const (x Type) | |
859 | popFront(); | |
860 | put( "const(" ); | |
861 | parseType(); | |
862 | put( ')' ); | |
863 | pad( name ); | |
864 | return dst[beg .. len]; | |
865 | case 'y': // Immutable (y Type) | |
866 | popFront(); | |
867 | put( "immutable(" ); | |
868 | parseType(); | |
869 | put( ')' ); | |
870 | pad( name ); | |
871 | return dst[beg .. len]; | |
872 | case 'N': | |
873 | popFront(); | |
874 | switch ( front ) | |
875 | { | |
876 | case 'g': // Wild (Ng Type) | |
877 | popFront(); | |
878 | // TODO: Anything needed here? | |
879 | put( "inout(" ); | |
880 | parseType(); | |
881 | put( ')' ); | |
882 | return dst[beg .. len]; | |
883 | case 'h': // TypeVector (Nh Type) | |
884 | popFront(); | |
885 | put( "__vector(" ); | |
886 | parseType(); | |
887 | put( ')' ); | |
888 | return dst[beg .. len]; | |
889 | default: | |
890 | error(); | |
891 | assert( 0 ); | |
892 | } | |
893 | case 'A': // TypeArray (A Type) | |
894 | popFront(); | |
895 | parseType(); | |
896 | put( "[]" ); | |
897 | pad( name ); | |
898 | return dst[beg .. len]; | |
899 | case 'G': // TypeStaticArray (G Number Type) | |
900 | popFront(); | |
901 | auto num = sliceNumber(); | |
902 | parseType(); | |
903 | put( '[' ); | |
904 | put( num ); | |
905 | put( ']' ); | |
906 | pad( name ); | |
907 | return dst[beg .. len]; | |
908 | case 'H': // TypeAssocArray (H Type Type) | |
909 | popFront(); | |
910 | // skip t1 | |
911 | auto tx = parseType(); | |
912 | parseType(); | |
913 | put( '[' ); | |
914 | put( tx ); | |
915 | put( ']' ); | |
916 | pad( name ); | |
917 | return dst[beg .. len]; | |
918 | case 'P': // TypePointer (P Type) | |
919 | popFront(); | |
920 | parseType(); | |
921 | put( '*' ); | |
922 | pad( name ); | |
923 | return dst[beg .. len]; | |
924 | case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction | |
925 | return parseTypeFunction( name ); | |
b4c522fa IB |
926 | case 'C': // TypeClass (C LName) |
927 | case 'S': // TypeStruct (S LName) | |
928 | case 'E': // TypeEnum (E LName) | |
929 | case 'T': // TypeTypedef (T LName) | |
930 | popFront(); | |
931 | parseQualifiedName(); | |
932 | pad( name ); | |
933 | return dst[beg .. len]; | |
934 | case 'D': // TypeDelegate (D TypeFunction) | |
935 | popFront(); | |
936 | auto modbeg = len; | |
937 | parseModifier(); | |
938 | auto modend = len; | |
939 | if ( front == 'Q' ) | |
940 | parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) ); | |
941 | else | |
942 | parseTypeFunction( name, IsDelegate.yes ); | |
943 | if (modend > modbeg) | |
944 | { | |
945 | // move modifiers behind the function arguments | |
946 | shift(dst[modend-1 .. modend]); // trailing space | |
947 | shift(dst[modbeg .. modend-1]); | |
948 | } | |
949 | return dst[beg .. len]; | |
950 | case 'n': // TypeNone (n) | |
951 | popFront(); | |
952 | // TODO: Anything needed here? | |
953 | return dst[beg .. len]; | |
954 | case 'B': // TypeTuple (B Number Arguments) | |
955 | popFront(); | |
956 | // TODO: Handle this. | |
957 | return dst[beg .. len]; | |
958 | case 'Z': // Internal symbol | |
959 | // This 'type' is used for untyped internal symbols, i.e.: | |
960 | // __array | |
961 | // __init | |
962 | // __vtbl | |
963 | // __Class | |
964 | // __Interface | |
965 | // __ModuleInfo | |
966 | popFront(); | |
967 | return dst[beg .. len]; | |
968 | default: | |
969 | if (t >= 'a' && t <= 'w') | |
970 | { | |
971 | popFront(); | |
972 | put( primitives[cast(size_t)(t - 'a')] ); | |
973 | pad( name ); | |
974 | return dst[beg .. len]; | |
975 | } | |
976 | else if (t == 'z') | |
977 | { | |
978 | popFront(); | |
979 | switch ( front ) | |
980 | { | |
981 | case 'i': | |
982 | popFront(); | |
983 | put( "cent" ); | |
984 | pad( name ); | |
985 | return dst[beg .. len]; | |
986 | case 'k': | |
987 | popFront(); | |
988 | put( "ucent" ); | |
989 | pad( name ); | |
990 | return dst[beg .. len]; | |
991 | default: | |
992 | error(); | |
993 | assert( 0 ); | |
994 | } | |
995 | } | |
996 | error(); | |
997 | return null; | |
998 | } | |
999 | } | |
1000 | ||
1001 | ||
1002 | /* | |
1003 | TypeFunction: | |
1004 | CallConvention FuncAttrs Arguments ArgClose Type | |
1005 | ||
1006 | CallConvention: | |
1007 | F // D | |
1008 | U // C | |
1009 | W // Windows | |
b4c522fa IB |
1010 | R // C++ |
1011 | ||
1012 | FuncAttrs: | |
1013 | FuncAttr | |
1014 | FuncAttr FuncAttrs | |
1015 | ||
1016 | FuncAttr: | |
1017 | empty | |
1018 | FuncAttrPure | |
1019 | FuncAttrNothrow | |
1020 | FuncAttrProperty | |
1021 | FuncAttrRef | |
1022 | FuncAttrReturn | |
1023 | FuncAttrScope | |
1024 | FuncAttrTrusted | |
1025 | FuncAttrSafe | |
1026 | ||
1027 | FuncAttrPure: | |
1028 | Na | |
1029 | ||
1030 | FuncAttrNothrow: | |
1031 | Nb | |
1032 | ||
1033 | FuncAttrRef: | |
1034 | Nc | |
1035 | ||
1036 | FuncAttrProperty: | |
1037 | Nd | |
1038 | ||
1039 | FuncAttrTrusted: | |
1040 | Ne | |
1041 | ||
1042 | FuncAttrSafe: | |
1043 | Nf | |
1044 | ||
1045 | FuncAttrNogc: | |
1046 | Ni | |
1047 | ||
1048 | FuncAttrReturn: | |
1049 | Nj | |
1050 | ||
1051 | FuncAttrScope: | |
1052 | Nl | |
1053 | ||
1054 | Arguments: | |
1055 | Argument | |
1056 | Argument Arguments | |
1057 | ||
1058 | Argument: | |
1059 | Argument2 | |
1060 | M Argument2 // scope | |
1061 | ||
1062 | Argument2: | |
1063 | Type | |
1064 | J Type // out | |
1065 | K Type // ref | |
1066 | L Type // lazy | |
1067 | ||
1068 | ArgClose | |
1069 | X // variadic T t,...) style | |
1070 | Y // variadic T t...) style | |
1071 | Z // not variadic | |
1072 | */ | |
1073 | void parseCallConvention() | |
1074 | { | |
1075 | // CallConvention | |
1076 | switch ( front ) | |
1077 | { | |
1078 | case 'F': // D | |
1079 | popFront(); | |
1080 | break; | |
1081 | case 'U': // C | |
1082 | popFront(); | |
1083 | put( "extern (C) " ); | |
1084 | break; | |
1085 | case 'W': // Windows | |
1086 | popFront(); | |
1087 | put( "extern (Windows) " ); | |
1088 | break; | |
b4c522fa IB |
1089 | case 'R': // C++ |
1090 | popFront(); | |
1091 | put( "extern (C++) " ); | |
1092 | break; | |
1093 | default: | |
1094 | error(); | |
1095 | } | |
1096 | } | |
1097 | ||
1098 | void parseModifier() | |
1099 | { | |
1100 | switch ( front ) | |
1101 | { | |
1102 | case 'y': | |
1103 | popFront(); | |
1104 | put( "immutable " ); | |
1105 | break; | |
1106 | case 'O': | |
1107 | popFront(); | |
1108 | put( "shared " ); | |
1109 | if ( front == 'x' ) | |
1110 | goto case 'x'; | |
1111 | if ( front == 'N' ) | |
1112 | goto case 'N'; | |
1113 | break; | |
1114 | case 'N': | |
1115 | if ( peek( 1 ) != 'g' ) | |
1116 | break; | |
1117 | popFront(); | |
1118 | popFront(); | |
1119 | put( "inout " ); | |
1120 | if ( front == 'x' ) | |
1121 | goto case 'x'; | |
1122 | break; | |
1123 | case 'x': | |
1124 | popFront(); | |
1125 | put( "const " ); | |
1126 | break; | |
1127 | default: break; | |
1128 | } | |
1129 | } | |
1130 | ||
1131 | void parseFuncAttr() | |
1132 | { | |
1133 | // FuncAttrs | |
1134 | breakFuncAttrs: | |
1135 | while ('N' == front) | |
1136 | { | |
1137 | popFront(); | |
1138 | switch ( front ) | |
1139 | { | |
1140 | case 'a': // FuncAttrPure | |
1141 | popFront(); | |
1142 | put( "pure " ); | |
1143 | continue; | |
1144 | case 'b': // FuncAttrNoThrow | |
1145 | popFront(); | |
1146 | put( "nothrow " ); | |
1147 | continue; | |
1148 | case 'c': // FuncAttrRef | |
1149 | popFront(); | |
1150 | put( "ref " ); | |
1151 | continue; | |
1152 | case 'd': // FuncAttrProperty | |
1153 | popFront(); | |
1154 | put( "@property " ); | |
1155 | continue; | |
1156 | case 'e': // FuncAttrTrusted | |
1157 | popFront(); | |
1158 | put( "@trusted " ); | |
1159 | continue; | |
1160 | case 'f': // FuncAttrSafe | |
1161 | popFront(); | |
1162 | put( "@safe " ); | |
1163 | continue; | |
1164 | case 'g': | |
1165 | case 'h': | |
1166 | case 'k': | |
1167 | // NOTE: The inout parameter type is represented as "Ng". | |
1168 | // The vector parameter type is represented as "Nh". | |
1169 | // The return parameter type is represented as "Nk". | |
1170 | // These make it look like a FuncAttr, but infact | |
1171 | // if we see these, then we know we're really in | |
1172 | // the parameter list. Rewind and break. | |
1173 | pos--; | |
1174 | break breakFuncAttrs; | |
1175 | case 'i': // FuncAttrNogc | |
1176 | popFront(); | |
1177 | put( "@nogc " ); | |
1178 | continue; | |
1179 | case 'j': // FuncAttrReturn | |
1180 | popFront(); | |
1181 | put( "return " ); | |
1182 | continue; | |
1183 | case 'l': // FuncAttrScope | |
1184 | popFront(); | |
1185 | put( "scope " ); | |
1186 | continue; | |
5fee5ec3 IB |
1187 | case 'm': // FuncAttrLive |
1188 | popFront(); | |
1189 | put( "@live " ); | |
1190 | continue; | |
b4c522fa IB |
1191 | default: |
1192 | error(); | |
1193 | } | |
1194 | } | |
1195 | } | |
1196 | ||
5fee5ec3 | 1197 | void parseFuncArguments() scope |
b4c522fa IB |
1198 | { |
1199 | // Arguments | |
1200 | for ( size_t n = 0; true; n++ ) | |
1201 | { | |
1202 | debug(info) printf( "tok (%c)\n", front ); | |
1203 | switch ( front ) | |
1204 | { | |
1205 | case 'X': // ArgClose (variadic T t...) style) | |
1206 | popFront(); | |
1207 | put( "..." ); | |
1208 | return; | |
1209 | case 'Y': // ArgClose (variadic T t,...) style) | |
1210 | popFront(); | |
1211 | put( ", ..." ); | |
1212 | return; | |
1213 | case 'Z': // ArgClose (not variadic) | |
1214 | popFront(); | |
1215 | return; | |
1216 | default: | |
1217 | break; | |
1218 | } | |
1219 | putComma(n); | |
1220 | if ( 'M' == front ) | |
1221 | { | |
1222 | popFront(); | |
1223 | put( "scope " ); | |
1224 | } | |
1225 | if ( 'N' == front ) | |
1226 | { | |
1227 | popFront(); | |
1228 | if ( 'k' == front ) // Return (Nk Parameter2) | |
1229 | { | |
1230 | popFront(); | |
1231 | put( "return " ); | |
1232 | } | |
1233 | else | |
1234 | pos--; | |
1235 | } | |
1236 | switch ( front ) | |
1237 | { | |
5fee5ec3 | 1238 | case 'I': // in (I Type) |
b4c522fa | 1239 | popFront(); |
5fee5ec3 IB |
1240 | put("in "); |
1241 | if (front == 'K') | |
1242 | goto case; | |
b4c522fa IB |
1243 | parseType(); |
1244 | continue; | |
1245 | case 'K': // ref (K Type) | |
1246 | popFront(); | |
1247 | put( "ref " ); | |
1248 | parseType(); | |
1249 | continue; | |
5fee5ec3 IB |
1250 | case 'J': // out (J Type) |
1251 | popFront(); | |
1252 | put( "out " ); | |
1253 | parseType(); | |
1254 | continue; | |
b4c522fa IB |
1255 | case 'L': // lazy (L Type) |
1256 | popFront(); | |
1257 | put( "lazy " ); | |
1258 | parseType(); | |
1259 | continue; | |
1260 | default: | |
1261 | parseType(); | |
1262 | } | |
1263 | } | |
1264 | } | |
1265 | ||
1266 | enum IsDelegate { no, yes } | |
1267 | ||
1268 | /* | |
1269 | TypeFunction: | |
1270 | CallConvention FuncAttrs Arguments ArgClose Type | |
1271 | */ | |
5fee5ec3 | 1272 | char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return |
b4c522fa IB |
1273 | { |
1274 | debug(trace) printf( "parseTypeFunction+\n" ); | |
1275 | debug(trace) scope(success) printf( "parseTypeFunction-\n" ); | |
1276 | auto beg = len; | |
1277 | ||
1278 | parseCallConvention(); | |
1279 | auto attrbeg = len; | |
1280 | parseFuncAttr(); | |
1281 | ||
1282 | auto argbeg = len; | |
1283 | put( '(' ); | |
1284 | parseFuncArguments(); | |
1285 | put( ')' ); | |
1286 | if (attrbeg < argbeg) | |
1287 | { | |
1288 | // move function attributes behind arguments | |
1289 | shift( dst[argbeg - 1 .. argbeg] ); // trailing space | |
1290 | shift( dst[attrbeg .. argbeg - 1] ); // attributes | |
1291 | argbeg = attrbeg; | |
1292 | } | |
1293 | auto retbeg = len; | |
1294 | parseType(); | |
1295 | put( ' ' ); | |
1296 | // append name/delegate/function | |
1297 | if ( name.length ) | |
1298 | { | |
1299 | if ( !contains( dst[0 .. len], name ) ) | |
1300 | put( name ); | |
1301 | else if ( shift( name ).ptr != name.ptr ) | |
1302 | { | |
1303 | argbeg -= name.length; | |
1304 | retbeg -= name.length; | |
1305 | } | |
1306 | } | |
1307 | else if ( IsDelegate.yes == isdg ) | |
1308 | put( "delegate" ); | |
1309 | else | |
1310 | put( "function" ); | |
1311 | // move arguments and attributes behind name | |
1312 | shift( dst[argbeg .. retbeg] ); | |
1313 | return dst[beg..len]; | |
1314 | } | |
1315 | ||
1316 | static bool isCallConvention( char ch ) | |
1317 | { | |
1318 | switch ( ch ) | |
1319 | { | |
1320 | case 'F', 'U', 'V', 'W', 'R': | |
1321 | return true; | |
1322 | default: | |
1323 | return false; | |
1324 | } | |
1325 | } | |
1326 | ||
1327 | /* | |
1328 | Value: | |
1329 | n | |
1330 | Number | |
1331 | i Number | |
1332 | N Number | |
1333 | e HexFloat | |
1334 | c HexFloat c HexFloat | |
1335 | A Number Value... | |
1336 | ||
1337 | HexFloat: | |
1338 | NAN | |
1339 | INF | |
1340 | NINF | |
1341 | N HexDigits P Exponent | |
1342 | HexDigits P Exponent | |
1343 | ||
1344 | Exponent: | |
1345 | N Number | |
1346 | Number | |
1347 | ||
1348 | HexDigits: | |
1349 | HexDigit | |
1350 | HexDigit HexDigits | |
1351 | ||
1352 | HexDigit: | |
1353 | Digit | |
1354 | A | |
1355 | B | |
1356 | C | |
1357 | D | |
1358 | E | |
1359 | F | |
1360 | */ | |
5fee5ec3 | 1361 | void parseValue(scope char[] name = null, char type = '\0' ) scope |
b4c522fa IB |
1362 | { |
1363 | debug(trace) printf( "parseValue+\n" ); | |
1364 | debug(trace) scope(success) printf( "parseValue-\n" ); | |
1365 | ||
1366 | // printf( "*** %c\n", front ); | |
1367 | switch ( front ) | |
1368 | { | |
1369 | case 'n': | |
1370 | popFront(); | |
1371 | put( "null" ); | |
1372 | return; | |
1373 | case 'i': | |
1374 | popFront(); | |
1375 | if ( '0' > front || '9' < front ) | |
1376 | error( "Number expected" ); | |
1377 | goto case; | |
1378 | case '0': .. case '9': | |
1379 | parseIntegerValue( name, type ); | |
1380 | return; | |
1381 | case 'N': | |
1382 | popFront(); | |
1383 | put( '-' ); | |
1384 | parseIntegerValue( name, type ); | |
1385 | return; | |
1386 | case 'e': | |
1387 | popFront(); | |
1388 | parseReal(); | |
1389 | return; | |
1390 | case 'c': | |
1391 | popFront(); | |
1392 | parseReal(); | |
1393 | put( '+' ); | |
1394 | match( 'c' ); | |
1395 | parseReal(); | |
1396 | put( 'i' ); | |
1397 | return; | |
1398 | case 'a': case 'w': case 'd': | |
1399 | char t = front; | |
1400 | popFront(); | |
1401 | auto n = decodeNumber(); | |
1402 | match( '_' ); | |
1403 | put( '"' ); | |
1404 | foreach (i; 0..n) | |
1405 | { | |
1406 | auto a = ascii2hex( front ); popFront(); | |
1407 | auto b = ascii2hex( front ); popFront(); | |
1408 | auto v = cast(char)((a << 4) | b); | |
1409 | if (' ' <= v && v <= '~') // ASCII printable | |
1410 | { | |
1411 | put(v); | |
1412 | } | |
1413 | else | |
1414 | { | |
1415 | put("\\x"); | |
1416 | putAsHex(v, 2); | |
1417 | } | |
1418 | } | |
1419 | put( '"' ); | |
1420 | if ( 'a' != t ) | |
1421 | put(t); | |
1422 | return; | |
1423 | case 'A': | |
1424 | // NOTE: This is kind of a hack. An associative array literal | |
1425 | // [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type | |
1426 | // is "Hii" and the value is "A2i1i2i3i4". Thus the only | |
1427 | // way to determine that this is an AA value rather than an | |
1428 | // array value is for the caller to supply the type char. | |
1429 | // Hopefully, this will change so that the value is | |
1430 | // "H2i1i2i3i4", rendering this unnecesary. | |
1431 | if ( 'H' == type ) | |
1432 | goto LassocArray; | |
1433 | // A Number Value... | |
1434 | // An array literal. Value is repeated Number times. | |
1435 | popFront(); | |
1436 | put( '[' ); | |
1437 | auto n = decodeNumber(); | |
1438 | foreach ( i; 0 .. n ) | |
1439 | { | |
1440 | putComma(i); | |
1441 | parseValue(); | |
1442 | } | |
1443 | put( ']' ); | |
1444 | return; | |
1445 | case 'H': | |
1446 | LassocArray: | |
1447 | // H Number Value... | |
1448 | // An associative array literal. Value is repeated 2*Number times. | |
1449 | popFront(); | |
1450 | put( '[' ); | |
1451 | auto n = decodeNumber(); | |
1452 | foreach ( i; 0 .. n ) | |
1453 | { | |
1454 | putComma(i); | |
1455 | parseValue(); | |
1456 | put(':'); | |
1457 | parseValue(); | |
1458 | } | |
1459 | put( ']' ); | |
1460 | return; | |
1461 | case 'S': | |
1462 | // S Number Value... | |
1463 | // A struct literal. Value is repeated Number times. | |
1464 | popFront(); | |
1465 | if ( name.length ) | |
1466 | put( name ); | |
1467 | put( '(' ); | |
1468 | auto n = decodeNumber(); | |
1469 | foreach ( i; 0 .. n ) | |
1470 | { | |
1471 | putComma(i); | |
1472 | parseValue(); | |
1473 | } | |
1474 | put( ')' ); | |
1475 | return; | |
5fee5ec3 IB |
1476 | case 'f': |
1477 | // f MangledName | |
1478 | // A function literal symbol | |
1479 | popFront(); | |
1480 | parseMangledName(false, 1); | |
1481 | return; | |
b4c522fa IB |
1482 | default: |
1483 | error(); | |
1484 | } | |
1485 | } | |
1486 | ||
1487 | ||
5fee5ec3 | 1488 | void parseIntegerValue( scope char[] name = null, char type = '\0' ) scope |
b4c522fa IB |
1489 | { |
1490 | debug(trace) printf( "parseIntegerValue+\n" ); | |
1491 | debug(trace) scope(success) printf( "parseIntegerValue-\n" ); | |
1492 | ||
1493 | switch ( type ) | |
1494 | { | |
1495 | case 'a': // char | |
1496 | case 'u': // wchar | |
1497 | case 'w': // dchar | |
1498 | { | |
1499 | auto val = sliceNumber(); | |
1500 | auto num = decodeNumber( val ); | |
1501 | ||
1502 | switch ( num ) | |
1503 | { | |
1504 | case '\'': | |
1505 | put( "'\\''" ); | |
1506 | return; | |
1507 | // \", \? | |
1508 | case '\\': | |
1509 | put( "'\\\\'" ); | |
1510 | return; | |
1511 | case '\a': | |
1512 | put( "'\\a'" ); | |
1513 | return; | |
1514 | case '\b': | |
1515 | put( "'\\b'" ); | |
1516 | return; | |
1517 | case '\f': | |
1518 | put( "'\\f'" ); | |
1519 | return; | |
1520 | case '\n': | |
1521 | put( "'\\n'" ); | |
1522 | return; | |
1523 | case '\r': | |
1524 | put( "'\\r'" ); | |
1525 | return; | |
1526 | case '\t': | |
1527 | put( "'\\t'" ); | |
1528 | return; | |
1529 | case '\v': | |
1530 | put( "'\\v'" ); | |
1531 | return; | |
1532 | default: | |
1533 | switch ( type ) | |
1534 | { | |
1535 | case 'a': | |
1536 | if ( num >= 0x20 && num < 0x7F ) | |
1537 | { | |
1538 | put( '\'' ); | |
1539 | put( cast(char)num ); | |
1540 | put( '\'' ); | |
1541 | return; | |
1542 | } | |
1543 | put( "\\x" ); | |
1544 | putAsHex( num, 2 ); | |
1545 | return; | |
1546 | case 'u': | |
1547 | put( "'\\u" ); | |
1548 | putAsHex( num, 4 ); | |
1549 | put( '\'' ); | |
1550 | return; | |
1551 | case 'w': | |
1552 | put( "'\\U" ); | |
1553 | putAsHex( num, 8 ); | |
1554 | put( '\'' ); | |
1555 | return; | |
1556 | default: | |
1557 | assert( 0 ); | |
1558 | } | |
1559 | } | |
1560 | } | |
1561 | case 'b': // bool | |
1562 | put( decodeNumber() ? "true" : "false" ); | |
1563 | return; | |
1564 | case 'h', 't', 'k': // ubyte, ushort, uint | |
1565 | put( sliceNumber() ); | |
1566 | put( 'u' ); | |
1567 | return; | |
1568 | case 'l': // long | |
1569 | put( sliceNumber() ); | |
1570 | put( 'L' ); | |
1571 | return; | |
1572 | case 'm': // ulong | |
1573 | put( sliceNumber() ); | |
1574 | put( "uL" ); | |
1575 | return; | |
1576 | default: | |
1577 | put( sliceNumber() ); | |
1578 | return; | |
1579 | } | |
1580 | } | |
1581 | ||
1582 | ||
1583 | /* | |
1584 | TemplateArgs: | |
1585 | TemplateArg | |
1586 | TemplateArg TemplateArgs | |
1587 | ||
1588 | TemplateArg: | |
1589 | TemplateArgX | |
1590 | H TemplateArgX | |
1591 | ||
1592 | TemplateArgX: | |
1593 | T Type | |
1594 | V Type Value | |
1595 | S Number_opt QualifiedName | |
1596 | X ExternallyMangledName | |
1597 | */ | |
5fee5ec3 | 1598 | void parseTemplateArgs() scope |
b4c522fa IB |
1599 | { |
1600 | debug(trace) printf( "parseTemplateArgs+\n" ); | |
1601 | debug(trace) scope(success) printf( "parseTemplateArgs-\n" ); | |
1602 | ||
1603 | L_nextArg: | |
1604 | for ( size_t n = 0; true; n++ ) | |
1605 | { | |
1606 | if ( front == 'H' ) | |
1607 | popFront(); | |
1608 | ||
1609 | switch ( front ) | |
1610 | { | |
1611 | case 'T': | |
1612 | popFront(); | |
1613 | putComma(n); | |
1614 | parseType(); | |
1615 | continue; | |
1616 | case 'V': | |
1617 | popFront(); | |
1618 | putComma(n); | |
1619 | // NOTE: In the few instances where the type is actually | |
1620 | // desired in the output it should precede the value | |
1621 | // generated by parseValue, so it is safe to simply | |
1622 | // decrement len and let put/append do its thing. | |
1623 | char t = front; // peek at type for parseValue | |
1624 | if ( t == 'Q' ) | |
1625 | t = peekBackref(); | |
5fee5ec3 | 1626 | char[] name; silent( delegate void() { name = parseType(); } ); |
b4c522fa IB |
1627 | parseValue( name, t ); |
1628 | continue; | |
1629 | case 'S': | |
1630 | popFront(); | |
1631 | putComma(n); | |
1632 | ||
1633 | if ( mayBeMangledNameArg() ) | |
1634 | { | |
1635 | auto l = len; | |
1636 | auto p = pos; | |
1637 | auto b = brp; | |
1638 | try | |
1639 | { | |
1640 | debug(trace) printf( "may be mangled name arg\n" ); | |
1641 | parseMangledNameArg(); | |
1642 | continue; | |
1643 | } | |
1644 | catch ( ParseException e ) | |
1645 | { | |
1646 | len = l; | |
1647 | pos = p; | |
1648 | brp = b; | |
1649 | debug(trace) printf( "not a mangled name arg\n" ); | |
1650 | } | |
1651 | } | |
1652 | if ( isDigit( front ) && isDigit( peek( 1 ) ) ) | |
1653 | { | |
1654 | // ambiguity: length followed by qualified name (starting with number) | |
1655 | // try all possible pairs of numbers | |
1656 | auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName | |
1657 | pos--; | |
1658 | auto l = len; | |
1659 | auto p = pos; | |
1660 | auto b = brp; | |
1661 | while ( qlen > 0 ) | |
1662 | { | |
1663 | try | |
1664 | { | |
1665 | parseQualifiedName(); | |
1666 | if ( pos == p + qlen ) | |
1667 | continue L_nextArg; | |
1668 | } | |
1669 | catch ( ParseException e ) | |
1670 | { | |
1671 | } | |
1672 | qlen /= 10; // retry with one digit less | |
1673 | pos = --p; | |
1674 | len = l; | |
1675 | brp = b; | |
1676 | } | |
1677 | } | |
1678 | parseQualifiedName(); | |
1679 | continue; | |
1680 | case 'X': | |
1681 | popFront(); | |
1682 | putComma(n); | |
1683 | parseLName(); | |
1684 | continue; | |
1685 | default: | |
1686 | return; | |
1687 | } | |
1688 | } | |
1689 | } | |
1690 | ||
1691 | ||
1692 | bool mayBeMangledNameArg() | |
1693 | { | |
1694 | debug(trace) printf( "mayBeMangledNameArg+\n" ); | |
1695 | debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" ); | |
1696 | ||
1697 | auto p = pos; | |
1698 | scope(exit) pos = p; | |
1699 | if ( isDigit( buf[pos] ) ) | |
1700 | { | |
1701 | auto n = decodeNumber(); | |
1702 | return n >= 4 && | |
1703 | pos < buf.length && '_' == buf[pos++] && | |
1704 | pos < buf.length && 'D' == buf[pos++] && | |
1705 | isDigit( buf[pos] ); | |
1706 | } | |
1707 | else | |
1708 | { | |
1709 | return pos < buf.length && '_' == buf[pos++] && | |
1710 | pos < buf.length && 'D' == buf[pos++] && | |
1711 | isSymbolNameFront(); | |
1712 | } | |
1713 | } | |
1714 | ||
1715 | ||
1716 | void parseMangledNameArg() | |
1717 | { | |
1718 | debug(trace) printf( "parseMangledNameArg+\n" ); | |
1719 | debug(trace) scope(success) printf( "parseMangledNameArg-\n" ); | |
1720 | ||
1721 | size_t n = 0; | |
1722 | if ( isDigit( front ) ) | |
1723 | n = decodeNumber(); | |
1724 | parseMangledName( false, n ); | |
1725 | } | |
1726 | ||
1727 | ||
1728 | /* | |
1729 | TemplateInstanceName: | |
1730 | Number __T LName TemplateArgs Z | |
1731 | */ | |
5fee5ec3 | 1732 | void parseTemplateInstanceName(bool hasNumber) scope |
b4c522fa IB |
1733 | { |
1734 | debug(trace) printf( "parseTemplateInstanceName+\n" ); | |
1735 | debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" ); | |
1736 | ||
1737 | auto sav = pos; | |
1738 | auto saveBrp = brp; | |
1739 | scope(failure) | |
1740 | { | |
1741 | pos = sav; | |
1742 | brp = saveBrp; | |
1743 | } | |
1744 | auto n = hasNumber ? decodeNumber() : 0; | |
1745 | auto beg = pos; | |
1746 | match( "__T" ); | |
1747 | parseLName(); | |
1748 | put( "!(" ); | |
1749 | parseTemplateArgs(); | |
1750 | match( 'Z' ); | |
1751 | if ( hasNumber && pos - beg != n ) | |
1752 | error( "Template name length mismatch" ); | |
1753 | put( ')' ); | |
1754 | } | |
1755 | ||
1756 | ||
5fee5ec3 | 1757 | bool mayBeTemplateInstanceName() scope |
b4c522fa IB |
1758 | { |
1759 | debug(trace) printf( "mayBeTemplateInstanceName+\n" ); | |
1760 | debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" ); | |
1761 | ||
1762 | auto p = pos; | |
1763 | scope(exit) pos = p; | |
1764 | auto n = decodeNumber(); | |
1765 | return n >= 5 && | |
1766 | pos < buf.length && '_' == buf[pos++] && | |
1767 | pos < buf.length && '_' == buf[pos++] && | |
1768 | pos < buf.length && 'T' == buf[pos++]; | |
1769 | } | |
1770 | ||
1771 | ||
1772 | /* | |
1773 | SymbolName: | |
1774 | LName | |
1775 | TemplateInstanceName | |
1776 | */ | |
5fee5ec3 | 1777 | void parseSymbolName() scope |
b4c522fa IB |
1778 | { |
1779 | debug(trace) printf( "parseSymbolName+\n" ); | |
1780 | debug(trace) scope(success) printf( "parseSymbolName-\n" ); | |
1781 | ||
1782 | // LName -> Number | |
1783 | // TemplateInstanceName -> Number "__T" | |
1784 | switch ( front ) | |
1785 | { | |
1786 | case '_': | |
1787 | // no length encoding for templates for new mangling | |
1788 | parseTemplateInstanceName(false); | |
1789 | return; | |
1790 | ||
1791 | case '0': .. case '9': | |
1792 | if ( mayBeTemplateInstanceName() ) | |
1793 | { | |
1794 | auto t = len; | |
1795 | ||
1796 | try | |
1797 | { | |
1798 | debug(trace) printf( "may be template instance name\n" ); | |
1799 | parseTemplateInstanceName(true); | |
1800 | return; | |
1801 | } | |
1802 | catch ( ParseException e ) | |
1803 | { | |
1804 | debug(trace) printf( "not a template instance name\n" ); | |
1805 | len = t; | |
1806 | } | |
1807 | } | |
1808 | goto case; | |
1809 | case 'Q': | |
1810 | parseLName(); | |
1811 | return; | |
1812 | default: | |
1813 | error(); | |
1814 | } | |
1815 | } | |
1816 | ||
1817 | // parse optional function arguments as part of a symbol name, i.e without return type | |
1818 | // if keepAttr, the calling convention and function attributes are not discarded, but returned | |
5fee5ec3 | 1819 | char[] parseFunctionTypeNoReturn( bool keepAttr = false ) return scope |
b4c522fa IB |
1820 | { |
1821 | // try to demangle a function, in case we are pointing to some function local | |
1822 | auto prevpos = pos; | |
1823 | auto prevlen = len; | |
1824 | auto prevbrp = brp; | |
1825 | ||
1826 | char[] attr; | |
1827 | try | |
1828 | { | |
1829 | if ( 'M' == front ) | |
1830 | { | |
1831 | // do not emit "needs this" | |
1832 | popFront(); | |
1833 | parseModifier(); | |
1834 | } | |
1835 | if ( isCallConvention( front ) ) | |
1836 | { | |
1837 | // we don't want calling convention and attributes in the qualified name | |
1838 | parseCallConvention(); | |
1839 | parseFuncAttr(); | |
1840 | if ( keepAttr ) | |
1841 | { | |
1842 | attr = dst[prevlen .. len]; | |
1843 | } | |
1844 | else | |
1845 | { | |
1846 | len = prevlen; | |
1847 | } | |
1848 | ||
1849 | put( '(' ); | |
1850 | parseFuncArguments(); | |
1851 | put( ')' ); | |
1852 | } | |
1853 | } | |
1854 | catch ( ParseException ) | |
1855 | { | |
1856 | // not part of a qualified name, so back up | |
1857 | pos = prevpos; | |
1858 | len = prevlen; | |
1859 | brp = prevbrp; | |
1860 | attr = null; | |
1861 | } | |
1862 | return attr; | |
1863 | } | |
1864 | ||
1865 | /* | |
1866 | QualifiedName: | |
1867 | SymbolName | |
1868 | SymbolName QualifiedName | |
1869 | */ | |
5fee5ec3 | 1870 | char[] parseQualifiedName() return scope |
b4c522fa IB |
1871 | { |
1872 | debug(trace) printf( "parseQualifiedName+\n" ); | |
1873 | debug(trace) scope(success) printf( "parseQualifiedName-\n" ); | |
1874 | size_t beg = len; | |
1875 | size_t n = 0; | |
1876 | ||
1877 | do | |
1878 | { | |
1879 | if ( n++ ) | |
1880 | put( '.' ); | |
1881 | parseSymbolName(); | |
1882 | parseFunctionTypeNoReturn(); | |
1883 | ||
1884 | } while ( isSymbolNameFront() ); | |
1885 | return dst[beg .. len]; | |
1886 | } | |
1887 | ||
1888 | ||
1889 | /* | |
1890 | MangledName: | |
1891 | _D QualifiedName Type | |
1892 | _D QualifiedName M Type | |
1893 | */ | |
5fee5ec3 | 1894 | void parseMangledName( bool displayType, size_t n = 0 ) scope |
b4c522fa IB |
1895 | { |
1896 | debug(trace) printf( "parseMangledName+\n" ); | |
1897 | debug(trace) scope(success) printf( "parseMangledName-\n" ); | |
1898 | char[] name = null; | |
1899 | ||
1900 | auto end = pos + n; | |
1901 | ||
1902 | eat( '_' ); | |
1903 | match( 'D' ); | |
1904 | do | |
1905 | { | |
1906 | size_t beg = len; | |
1907 | size_t nameEnd = len; | |
1908 | char[] attr; | |
1909 | do | |
1910 | { | |
1911 | if ( attr ) | |
1912 | remove( attr ); // dump attributes of parent symbols | |
1913 | if ( beg != len ) | |
1914 | put( '.' ); | |
1915 | parseSymbolName(); | |
1916 | nameEnd = len; | |
1917 | attr = parseFunctionTypeNoReturn( displayType ); | |
1918 | ||
1919 | } while ( isSymbolNameFront() ); | |
1920 | ||
1921 | if ( displayType ) | |
1922 | { | |
1923 | attr = shift( attr ); | |
1924 | nameEnd = len - attr.length; // name includes function arguments | |
1925 | } | |
1926 | name = dst[beg .. nameEnd]; | |
1927 | ||
1928 | debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr ); | |
1929 | if ( 'M' == front ) | |
1930 | popFront(); // has 'this' pointer | |
1931 | ||
1932 | auto lastlen = len; | |
1933 | auto type = parseType(); | |
1934 | if ( displayType ) | |
1935 | { | |
1936 | if ( type.length ) | |
1937 | put( ' ' ); | |
1938 | // sort (name,attr,type) -> (attr,type,name) | |
1939 | shift( name ); | |
1940 | } | |
1941 | else | |
1942 | { | |
1943 | // remove type | |
1944 | assert( attr.length == 0 ); | |
1945 | len = lastlen; | |
1946 | } | |
1947 | if ( pos >= buf.length || (n != 0 && pos >= end) ) | |
1948 | return; | |
1949 | ||
1950 | switch ( front ) | |
1951 | { | |
1952 | case 'T': // terminators when used as template alias parameter | |
1953 | case 'V': | |
1954 | case 'S': | |
1955 | case 'Z': | |
1956 | return; | |
1957 | default: | |
1958 | } | |
1959 | put( '.' ); | |
1960 | ||
1961 | } while ( true ); | |
1962 | } | |
1963 | ||
1964 | void parseMangledName() | |
1965 | { | |
1966 | parseMangledName( AddType.yes == addType ); | |
1967 | } | |
1968 | ||
5fee5ec3 IB |
1969 | char[] copyInput() return scope |
1970 | { | |
1971 | if (dst.length < buf.length) | |
1972 | dst.length = buf.length; | |
1973 | char[] r = dst[0 .. buf.length]; | |
1974 | r[] = buf[]; | |
1975 | return r; | |
1976 | } | |
1977 | ||
1978 | char[] doDemangle(alias FUNC)() return scope | |
b4c522fa IB |
1979 | { |
1980 | while ( true ) | |
1981 | { | |
1982 | try | |
1983 | { | |
1984 | debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr ); | |
1985 | FUNC(); | |
1986 | return dst[0 .. len]; | |
1987 | } | |
1988 | catch ( OverflowException e ) | |
1989 | { | |
1990 | debug(trace) printf( "overflow... restarting\n" ); | |
1991 | auto a = minBufSize; | |
1992 | auto b = 2 * dst.length; | |
1993 | auto newsz = a < b ? b : a; | |
1994 | debug(info) printf( "growing dst to %lu bytes\n", newsz ); | |
1995 | dst.length = newsz; | |
1996 | pos = len = brp = 0; | |
1997 | continue; | |
1998 | } | |
1999 | catch ( ParseException e ) | |
2000 | { | |
2001 | debug(info) | |
2002 | { | |
2003 | auto msg = e.toString(); | |
2004 | printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); | |
2005 | } | |
5fee5ec3 | 2006 | return copyInput(); |
b4c522fa IB |
2007 | } |
2008 | catch ( Exception e ) | |
2009 | { | |
2010 | assert( false ); // no other exceptions thrown | |
2011 | } | |
2012 | } | |
2013 | } | |
2014 | ||
2015 | char[] demangleName() nothrow | |
2016 | { | |
2017 | return doDemangle!parseMangledName(); | |
2018 | } | |
2019 | ||
2020 | char[] demangleType() nothrow | |
2021 | { | |
2022 | return doDemangle!parseType(); | |
2023 | } | |
2024 | } | |
2025 | ||
2026 | ||
2027 | /** | |
2028 | * Demangles D mangled names. If it is not a D mangled name, it returns its | |
2029 | * argument name. | |
2030 | * | |
2031 | * Params: | |
2032 | * buf = The string to demangle. | |
2033 | * dst = An optional destination buffer. | |
2034 | * | |
2035 | * Returns: | |
2036 | * The demangled name or the original string if the name is not a mangled D | |
2037 | * name. | |
2038 | */ | |
5fee5ec3 | 2039 | char[] demangle(return scope const(char)[] buf, return scope char[] dst = null ) nothrow pure @safe |
b4c522fa | 2040 | { |
b4c522fa | 2041 | auto d = Demangle!()(buf, dst); |
5fee5ec3 IB |
2042 | // fast path (avoiding throwing & catching exception) for obvious |
2043 | // non-D mangled names | |
2044 | if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D")) | |
2045 | return d.copyInput(); | |
b4c522fa IB |
2046 | return d.demangleName(); |
2047 | } | |
2048 | ||
2049 | ||
2050 | /** | |
2051 | * Demangles a D mangled type. | |
2052 | * | |
2053 | * Params: | |
2054 | * buf = The string to demangle. | |
2055 | * dst = An optional destination buffer. | |
2056 | * | |
2057 | * Returns: | |
2058 | * The demangled type name or the original string if the name is not a | |
2059 | * mangled D type. | |
2060 | */ | |
2061 | char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe | |
2062 | { | |
2063 | auto d = Demangle!()(buf, dst); | |
2064 | return d.demangleType(); | |
2065 | } | |
2066 | ||
2067 | /** | |
2068 | * reencode a mangled symbol name that might include duplicate occurrences | |
2069 | * of the same identifier by replacing all but the first occurence with | |
2070 | * a back reference. | |
2071 | * | |
2072 | * Params: | |
2073 | * mangled = The mangled string representing the type | |
2074 | * | |
2075 | * Returns: | |
2076 | * The mangled name with deduplicated identifiers | |
2077 | */ | |
5fee5ec3 | 2078 | char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe |
b4c522fa IB |
2079 | { |
2080 | static struct PrependHooks | |
2081 | { | |
2082 | size_t lastpos; | |
2083 | char[] result; | |
2084 | size_t[const(char)[]] idpos; // identifier positions | |
2085 | ||
2086 | static struct Replacement | |
2087 | { | |
2088 | size_t pos; // postion in original mangled string | |
2089 | size_t respos; // postion in result string | |
2090 | } | |
2091 | Replacement [] replacements; | |
2092 | ||
2093 | pure @safe: | |
5fee5ec3 | 2094 | size_t positionInResult(size_t pos) scope |
b4c522fa IB |
2095 | { |
2096 | foreach_reverse (r; replacements) | |
2097 | if (pos >= r.pos) | |
2098 | return r.respos + pos - r.pos; | |
2099 | return pos; | |
2100 | } | |
2101 | ||
2102 | alias Remangle = Demangle!(PrependHooks); | |
2103 | ||
5fee5ec3 | 2104 | void flushPosition(ref Remangle d) scope |
b4c522fa IB |
2105 | { |
2106 | if (lastpos < d.pos) | |
2107 | { | |
2108 | result ~= d.buf[lastpos .. d.pos]; | |
2109 | } | |
2110 | else if (lastpos > d.pos) | |
2111 | { | |
2112 | // roll back to earlier position | |
2113 | while (replacements.length > 0 && replacements[$-1].pos > d.pos) | |
2114 | replacements = replacements[0 .. $-1]; | |
2115 | ||
2116 | if (replacements.length > 0) | |
2117 | result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos; | |
2118 | else | |
2119 | result.length = d.pos; | |
2120 | } | |
2121 | } | |
2122 | ||
5fee5ec3 | 2123 | bool parseLName(scope ref Remangle d) scope |
b4c522fa IB |
2124 | { |
2125 | flushPosition(d); | |
2126 | ||
2127 | auto reslen = result.length; | |
2128 | auto refpos = d.pos; | |
2129 | if (d.front == 'Q') | |
2130 | { | |
2131 | size_t npos; | |
2132 | { | |
2133 | scope(exit) result.length = reslen; // remove all intermediate additions | |
2134 | // only support identifier back references | |
2135 | d.popFront(); | |
2136 | size_t n = d.decodeBackref(); | |
2137 | if (!n || n > refpos) | |
2138 | d.error("invalid back reference"); | |
2139 | ||
2140 | auto savepos = d.pos; | |
2141 | scope(exit) d.pos = savepos; | |
2142 | size_t srcpos = refpos - n; | |
2143 | ||
2144 | auto idlen = d.decodeNumber(); | |
2145 | if (d.pos + idlen > d.buf.length) | |
2146 | d.error("invalid back reference"); | |
2147 | auto id = d.buf[d.pos .. d.pos + idlen]; | |
2148 | auto pid = id in idpos; | |
2149 | if (!pid) | |
2150 | d.error("invalid back reference"); | |
2151 | npos = positionInResult(*pid); | |
2152 | } | |
2153 | encodeBackref(reslen - npos); | |
5fee5ec3 IB |
2154 | const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675 |
2155 | replacements ~= Replacement(pos, result.length); | |
b4c522fa IB |
2156 | } |
2157 | else | |
2158 | { | |
2159 | auto n = d.decodeNumber(); | |
2160 | if (!n || n > d.buf.length || n > d.buf.length - d.pos) | |
2161 | d.error("LName too shot or too long"); | |
2162 | auto id = d.buf[d.pos .. d.pos + n]; | |
2163 | d.pos += n; | |
2164 | if (auto pid = id in idpos) | |
2165 | { | |
2166 | size_t npos = positionInResult(*pid); | |
2167 | result.length = reslen; | |
2168 | encodeBackref(reslen - npos); | |
5fee5ec3 IB |
2169 | const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675 |
2170 | replacements ~= Replacement(pos, result.length); | |
b4c522fa IB |
2171 | } |
2172 | else | |
2173 | { | |
2174 | idpos[id] = refpos; | |
2175 | result ~= d.buf[refpos .. d.pos]; | |
2176 | } | |
2177 | } | |
2178 | lastpos = d.pos; | |
2179 | return true; | |
2180 | } | |
2181 | ||
5fee5ec3 | 2182 | char[] parseType( ref Remangle d, char[] name = null ) return scope |
b4c522fa IB |
2183 | { |
2184 | if (d.front != 'Q') | |
2185 | return null; | |
2186 | ||
2187 | flushPosition(d); | |
2188 | ||
2189 | auto refPos = d.pos; | |
2190 | d.popFront(); | |
2191 | auto n = d.decodeBackref(); | |
2192 | if (n == 0 || n > refPos) | |
2193 | d.error("invalid back reference"); | |
2194 | ||
2195 | size_t npos = positionInResult(refPos - n); | |
2196 | size_t reslen = result.length; | |
2197 | encodeBackref(reslen - npos); | |
2198 | ||
2199 | lastpos = d.pos; | |
2200 | return result[reslen .. $]; // anything but null | |
2201 | } | |
2202 | ||
5fee5ec3 | 2203 | void encodeBackref(size_t relpos) scope |
b4c522fa IB |
2204 | { |
2205 | result ~= 'Q'; | |
2206 | enum base = 26; | |
2207 | size_t div = 1; | |
2208 | while (relpos >= div * base) | |
2209 | div *= base; | |
2210 | while (div >= base) | |
2211 | { | |
2212 | auto dig = (relpos / div); | |
2213 | result ~= cast(char)('A' + dig); | |
2214 | relpos -= dig * div; | |
2215 | div /= base; | |
2216 | } | |
2217 | result ~= cast(char)('a' + relpos); | |
2218 | } | |
2219 | } | |
2220 | ||
2221 | auto d = Demangle!(PrependHooks)(mangled, null); | |
2222 | d.hooks = PrependHooks(); | |
2223 | d.mute = true; // no demangled output | |
2224 | try | |
2225 | { | |
2226 | d.parseMangledName(); | |
2227 | if (d.hooks.lastpos < d.pos) | |
2228 | d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos]; | |
2229 | return d.hooks.result; | |
2230 | } | |
2231 | catch (Exception) | |
2232 | { | |
2233 | // overflow exception cannot occur | |
2234 | return mangled.dup; | |
2235 | } | |
2236 | } | |
2237 | ||
2238 | /** | |
2239 | * Mangles a D symbol. | |
2240 | * | |
2241 | * Params: | |
2242 | * T = The type of the symbol. | |
2243 | * fqn = The fully qualified name of the symbol. | |
2244 | * dst = An optional destination buffer. | |
2245 | * | |
2246 | * Returns: | |
2247 | * The mangled name for a symbols of type T and the given fully | |
2248 | * qualified name. | |
2249 | */ | |
5fee5ec3 | 2250 | char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow |
b4c522fa IB |
2251 | { |
2252 | import core.internal.string : numDigits, unsignedToTempString; | |
2253 | ||
2254 | static struct DotSplitter | |
2255 | { | |
2256 | @safe pure nothrow: | |
2257 | const(char)[] s; | |
2258 | ||
2259 | @property bool empty() const { return !s.length; } | |
2260 | ||
5fee5ec3 | 2261 | @property const(char)[] front() const return |
b4c522fa IB |
2262 | { |
2263 | immutable i = indexOfDot(); | |
2264 | return i == -1 ? s[0 .. $] : s[0 .. i]; | |
2265 | } | |
2266 | ||
5fee5ec3 | 2267 | void popFront() scope |
b4c522fa IB |
2268 | { |
2269 | immutable i = indexOfDot(); | |
2270 | s = i == -1 ? s[$ .. $] : s[i+1 .. $]; | |
2271 | } | |
2272 | ||
5fee5ec3 | 2273 | private ptrdiff_t indexOfDot() const scope |
b4c522fa IB |
2274 | { |
2275 | foreach (i, c; s) if (c == '.') return i; | |
2276 | return -1; | |
2277 | } | |
2278 | } | |
2279 | ||
2280 | size_t len = "_D".length; | |
2281 | foreach (comp; DotSplitter(fqn)) | |
2282 | len += numDigits(comp.length) + comp.length; | |
2283 | len += T.mangleof.length; | |
2284 | if (dst.length < len) dst.length = len; | |
2285 | ||
2286 | size_t i = "_D".length; | |
2287 | dst[0 .. i] = "_D"; | |
2288 | foreach (comp; DotSplitter(fqn)) | |
2289 | { | |
2290 | const ndigits = numDigits(comp.length); | |
2291 | unsignedToTempString(comp.length, dst[i .. i + ndigits]); | |
2292 | i += ndigits; | |
2293 | dst[i .. i + comp.length] = comp[]; | |
2294 | i += comp.length; | |
2295 | } | |
2296 | dst[i .. i + T.mangleof.length] = T.mangleof[]; | |
2297 | i += T.mangleof.length; | |
2298 | ||
2299 | static if (hasTypeBackRef) | |
2300 | return reencodeMangled(dst[0 .. i]); | |
2301 | else | |
2302 | return dst[0 .. i]; | |
2303 | } | |
2304 | ||
2305 | ||
2306 | /// | |
2307 | @safe pure nothrow unittest | |
2308 | { | |
2309 | assert(mangle!int("a.b") == "_D1a1bi"); | |
2310 | assert(mangle!(char[])("test.foo") == "_D4test3fooAa"); | |
2311 | assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi"); | |
2312 | } | |
2313 | ||
2314 | @safe pure nothrow unittest | |
2315 | { | |
2316 | static assert(mangle!int("a.b") == "_D1a1bi"); | |
2317 | ||
2318 | auto buf = new char[](10); | |
2319 | buf = mangle!int("a.b", buf); | |
2320 | assert(buf == "_D1a1bi"); | |
2321 | buf = mangle!(char[])("test.foo", buf); | |
2322 | assert(buf == "_D4test3fooAa"); | |
2323 | buf = mangle!(real delegate(int))("modµ.dg"); | |
2324 | assert(buf == "_D5modµ2dgDFiZe", buf); | |
2325 | } | |
2326 | ||
2327 | ||
2328 | /** | |
2329 | * Mangles a D function. | |
2330 | * | |
2331 | * Params: | |
2332 | * T = function pointer type. | |
2333 | * fqn = The fully qualified name of the symbol. | |
2334 | * dst = An optional destination buffer. | |
2335 | * | |
2336 | * Returns: | |
2337 | * The mangled name for a function with function pointer type T and | |
2338 | * the given fully qualified name. | |
2339 | */ | |
5fee5ec3 | 2340 | char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function)) |
b4c522fa IB |
2341 | { |
2342 | static if (isExternD!FT) | |
2343 | { | |
2344 | return mangle!FT(fqn, dst); | |
2345 | } | |
2346 | else static if (hasPlainMangling!FT) | |
2347 | { | |
2348 | dst.length = fqn.length; | |
2349 | dst[] = fqn[]; | |
2350 | return dst; | |
2351 | } | |
2352 | else static if (isExternCPP!FT) | |
2353 | { | |
2354 | static assert(0, "Can't mangle extern(C++) functions."); | |
2355 | } | |
2356 | else | |
2357 | { | |
2358 | static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~")."); | |
2359 | } | |
2360 | } | |
2361 | ||
2362 | private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi"; | |
2363 | ||
b4c522fa IB |
2364 | @safe pure nothrow unittest |
2365 | { | |
2366 | assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi"); | |
2367 | static if (hasTypeBackRef) | |
2368 | { | |
2369 | assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi"); | |
2370 | assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi"); | |
2371 | } | |
2372 | else | |
2373 | { | |
2374 | auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals"); | |
2375 | assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi"); | |
2376 | auto remngl = reencodeMangled(mngl); | |
2377 | assert(remngl == "_D6object6Object8opEqualsFCQsZi"); | |
2378 | } | |
2379 | // trigger back tracking with ambiguity on '__T', template or identifier | |
2380 | assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); | |
2381 | } | |
2382 | ||
2383 | @safe pure nothrow unittest | |
2384 | { | |
2385 | int function(lazy int[], ...) fp; | |
2386 | assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi"); | |
2387 | assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi"); | |
2388 | } | |
2389 | ||
2390 | private template isExternD(FT) if (is(FT == function)) | |
2391 | { | |
2392 | enum isExternD = __traits(getLinkage, FT) == "D"; | |
2393 | } | |
2394 | ||
2395 | private template isExternCPP(FT) if (is(FT == function)) | |
2396 | { | |
2397 | enum isExternCPP = __traits(getLinkage, FT) == "C++"; | |
2398 | } | |
2399 | ||
2400 | private template hasPlainMangling(FT) if (is(FT == function)) | |
2401 | { | |
2402 | enum lnk = __traits(getLinkage, FT); | |
f886c4a7 IB |
2403 | // C || Windows |
2404 | enum hasPlainMangling = lnk == "C" || lnk == "Windows"; | |
b4c522fa IB |
2405 | } |
2406 | ||
2407 | @safe pure nothrow unittest | |
2408 | { | |
2409 | static extern(D) void fooD(); | |
2410 | static extern(C) void fooC(); | |
b4c522fa IB |
2411 | static extern(Windows) void fooW(); |
2412 | static extern(C++) void fooCPP(); | |
2413 | ||
2414 | bool check(FT)(bool isD, bool isCPP, bool isPlain) | |
2415 | { | |
2416 | return isExternD!FT == isD && isExternCPP!FT == isCPP && | |
2417 | hasPlainMangling!FT == isPlain; | |
2418 | } | |
2419 | static assert(check!(typeof(fooD))(true, false, false)); | |
2420 | static assert(check!(typeof(fooC))(false, false, true)); | |
b4c522fa IB |
2421 | static assert(check!(typeof(fooW))(false, false, true)); |
2422 | static assert(check!(typeof(fooCPP))(false, true, false)); | |
2423 | ||
2424 | static assert(__traits(compiles, mangleFunc!(typeof(&fooD))(""))); | |
2425 | static assert(__traits(compiles, mangleFunc!(typeof(&fooC))(""))); | |
b4c522fa IB |
2426 | static assert(__traits(compiles, mangleFunc!(typeof(&fooW))(""))); |
2427 | static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))(""))); | |
2428 | } | |
2429 | ||
2430 | /*** | |
2431 | * C name mangling is done by adding a prefix on some platforms. | |
2432 | */ | |
2433 | version (Win32) | |
2434 | enum string cPrefix = "_"; | |
2435 | else version (Darwin) | |
2436 | enum string cPrefix = "_"; | |
2437 | else | |
2438 | enum string cPrefix = ""; | |
2439 | ||
5fee5ec3 | 2440 | @safe pure nothrow unittest |
b4c522fa IB |
2441 | { |
2442 | immutable string[2][] table = | |
2443 | [ | |
2444 | ["printf", "printf"], | |
2445 | ["_foo", "_foo"], | |
2446 | ["_D88", "_D88"], | |
5fee5ec3 IB |
2447 | ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ], |
2448 | ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ], | |
b4c522fa IB |
2449 | ["_D4test3fooAa", "char[] test.foo"], |
2450 | ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"], | |
2451 | ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"], | |
2452 | ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"], | |
2453 | ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"], | |
2454 | //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""], | |
2455 | //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""], | |
2456 | ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"], | |
2457 | ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"], | |
2458 | ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"], | |
2459 | ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"], | |
2460 | ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"], | |
2461 | ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"], | |
2462 | ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"], | |
2463 | ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"], | |
2464 | ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"], | |
2465 | ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"], | |
2466 | ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"], | |
2467 | ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"], | |
2468 | ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"], | |
2469 | ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"], | |
2470 | ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"], | |
2471 | ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"], | |
2472 | ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"], | |
2473 | ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"], | |
2474 | ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"], | |
2475 | ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"], | |
2476 | ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"], | |
2477 | ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv", | |
2478 | "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"], | |
2479 | ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test", | |
2480 | "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"], | |
2481 | ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"], | |
2482 | ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"], | |
2483 | ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"], | |
2484 | ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"], | |
2485 | ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"], | |
2486 | ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"], | |
2487 | ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"], | |
2488 | ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"], | |
2489 | ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"], | |
2490 | ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"], | |
2491 | ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"], | |
2492 | ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"], | |
2493 | ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"], | |
2494 | ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"], | |
2495 | ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"], | |
2496 | ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"], | |
2497 | ["_D3foo7__arrayZ", "foo.__array"], | |
2498 | ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], | |
2499 | ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], | |
5fee5ec3 | 2500 | ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`], |
b4c522fa IB |
2501 | ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"], |
2502 | ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"], | |
2503 | ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"], | |
2504 | ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"], | |
2505 | ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"], | |
2506 | ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt", | |
2507 | "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"], | |
2508 | ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi", | |
2509 | "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"], | |
2510 | ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv", | |
2511 | "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"], | |
5fee5ec3 IB |
2512 | ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp", |
2513 | "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"], | |
b4c522fa IB |
2514 | // Lname '0' |
2515 | ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq", | |
2516 | "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, " | |
2517 | ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"], | |
2518 | ||
2519 | // back references | |
2520 | ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference | |
2521 | ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference | |
2522 | ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv", | |
2523 | "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"], | |
2524 | // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831 | |
2525 | ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv", | |
2526 | "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], | |
2527 | ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv", | |
2528 | "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], | |
f886c4a7 IB |
2529 | // formerly ambiguous on 'V', template value argument or pascal function |
2530 | // pascal functions have now been removed (in v2.095.0) | |
b4c522fa IB |
2531 | ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh", |
2532 | "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"], | |
2533 | // symbol back reference to location with symbol back reference | |
2534 | ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb", | |
2535 | "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(" | |
2536 | ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref " | |
2537 | ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"], | |
2538 | ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb", | |
2539 | "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], " | |
2540 | ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"], | |
2541 | ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm", | |
2542 | "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult." | |
2543 | ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"], | |
2544 | ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj", | |
2545 | "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef " | |
2546 | ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"], | |
2547 | ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult", | |
2548 | "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, " | |
2549 | ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)." | |
2550 | ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, " | |
2551 | ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"], | |
2552 | ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv", | |
2553 | "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"], | |
2554 | ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter | |
2555 | "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"], | |
2556 | // back reference for type in template AA parameter value | |
2557 | ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm", | |
2558 | `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").` | |
2559 | ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.` | |
2560 | ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`], | |
2561 | ]; | |
2562 | ||
2563 | ||
2564 | template staticIota(int x) | |
2565 | { | |
2566 | template Seq(T...){ alias Seq = T; } | |
2567 | ||
2568 | static if (x == 0) | |
2569 | alias staticIota = Seq!(); | |
2570 | else | |
2571 | alias staticIota = Seq!(staticIota!(x - 1), x - 1); | |
2572 | } | |
b4c522fa IB |
2573 | foreach ( i, name; table ) |
2574 | { | |
2575 | auto r = demangle( name[0] ); | |
2576 | assert( r == name[1], | |
5fee5ec3 | 2577 | "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`"); |
b4c522fa IB |
2578 | } |
2579 | foreach ( i; staticIota!(table.length) ) | |
2580 | { | |
2581 | enum r = demangle( table[i][0] ); | |
2582 | static assert( r == table[i][1], | |
5fee5ec3 IB |
2583 | "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`"); |
2584 | } | |
2585 | ||
2586 | { | |
2587 | // https://issues.dlang.org/show_bug.cgi?id=18531 | |
2588 | auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`; | |
2589 | auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`; | |
2590 | auto dst = new char[200]; | |
2591 | auto ret = demangle( symbol, dst); | |
2592 | assert( ret == demangled ); | |
b4c522fa IB |
2593 | } |
2594 | } | |
2595 | ||
5fee5ec3 IB |
2596 | unittest |
2597 | { | |
2598 | // https://issues.dlang.org/show_bug.cgi?id=18300 | |
2599 | string s = demangle.mangleof; | |
2600 | foreach (i; 1..77) | |
2601 | { | |
2602 | char[] buf = new char[i]; | |
2603 | auto ds = demangle(s, buf); | |
2604 | assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[])" || | |
2605 | ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[])"); | |
2606 | } | |
2607 | } | |
2608 | ||
2609 | unittest | |
2610 | { | |
2611 | // https://issues.dlang.org/show_bug.cgi?id=18300 | |
2612 | string s = "_D1"; | |
2613 | string expected = "int "; | |
2614 | foreach (_; 0..10_000) | |
2615 | { | |
2616 | s ~= "a1"; | |
2617 | expected ~= "a."; | |
2618 | } | |
2619 | s ~= "FiZi"; | |
2620 | expected ~= "F"; | |
2621 | assert(s.demangle == expected); | |
2622 | } | |
b4c522fa IB |
2623 | |
2624 | /* | |
2625 | * | |
2626 | */ | |
2627 | string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe | |
2628 | { | |
2629 | string s; | |
2630 | uint zlen, zpos; | |
2631 | ||
2632 | // decompress symbol | |
2633 | while ( p < ln.length ) | |
2634 | { | |
2635 | int ch = cast(ubyte) ln[p++]; | |
2636 | if ( (ch & 0xc0) == 0xc0 ) | |
2637 | { | |
2638 | zlen = (ch & 0x7) + 1; | |
2639 | zpos = ((ch >> 3) & 7) + 1; // + zlen; | |
2640 | if ( zpos > s.length ) | |
2641 | break; | |
2642 | s ~= s[$ - zpos .. $ - zpos + zlen]; | |
2643 | } | |
2644 | else if ( ch >= 0x80 ) | |
2645 | { | |
2646 | if ( p >= ln.length ) | |
2647 | break; | |
2648 | int ch2 = cast(ubyte) ln[p++]; | |
2649 | zlen = (ch2 & 0x7f) | ((ch & 0x38) << 4); | |
2650 | if ( p >= ln.length ) | |
2651 | break; | |
2652 | int ch3 = cast(ubyte) ln[p++]; | |
2653 | zpos = (ch3 & 0x7f) | ((ch & 7) << 7); | |
2654 | if ( zpos > s.length ) | |
2655 | break; | |
2656 | s ~= s[$ - zpos .. $ - zpos + zlen]; | |
2657 | } | |
2658 | else if ( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' ) | |
2659 | s ~= cast(char) ch; | |
2660 | else | |
2661 | { | |
2662 | p--; | |
2663 | break; | |
2664 | } | |
2665 | } | |
2666 | return s; | |
2667 | } | |
2668 | ||
2669 | // locally purified for internal use here only | |
2670 | extern (C) private | |
2671 | { | |
2672 | pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr); | |
2673 | ||
2674 | void fakePureReprintReal(char[] nptr) | |
2675 | { | |
2676 | import core.stdc.stdlib : strtold; | |
2677 | import core.stdc.stdio : snprintf; | |
2678 | import core.stdc.errno : errno; | |
2679 | ||
2680 | const err = errno; | |
2681 | real val = strtold(nptr.ptr, null); | |
2682 | snprintf(nptr.ptr, nptr.length, "%#Lg", val); | |
2683 | errno = err; | |
2684 | } | |
2685 | } |