blob: 73e45d655078258fe8f1e019f81da75bdfcf5065 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <cxxabi.h>
11
Greg Claytone41e5892010-09-03 23:26:12 +000012#include "llvm/ADT/DenseMap.h"
13
Chris Lattner30fdc8d2010-06-08 16:52:24 +000014#include "lldb/Core/ConstString.h"
15#include "lldb/Core/Mangled.h"
Greg Clayton83c5cd92010-11-14 22:13:40 +000016#include "lldb/Core/RegularExpression.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000017#include "lldb/Core/Stream.h"
18#include "lldb/Core/Timer.h"
Eli Friedman88966972010-06-09 08:50:27 +000019#include <ctype.h>
20#include <string.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021
22using namespace lldb_private;
23
Greg Clayton5e0c5e82012-07-18 20:47:40 +000024static inline bool
25cstring_is_mangled (const char *s)
26{
27 if (s)
28 return s[0] == '_' && s[1] == 'Z';
29 return false;
30}
31
Chris Lattner30fdc8d2010-06-08 16:52:24 +000032#pragma mark Mangled
33//----------------------------------------------------------------------
34// Default constructor
35//----------------------------------------------------------------------
36Mangled::Mangled () :
37 m_mangled(),
38 m_demangled()
39{
40}
41
42//----------------------------------------------------------------------
43// Constructor with an optional string and a boolean indicating if it is
44// the mangled version.
45//----------------------------------------------------------------------
46Mangled::Mangled (const char *s, bool mangled) :
47 m_mangled(),
48 m_demangled()
49{
50 if (s && s[0])
51 {
52 SetValue(s, mangled);
53 }
54}
55
56//----------------------------------------------------------------------
Greg Clayton5e0c5e82012-07-18 20:47:40 +000057// Constructor with an optional string and a boolean indicating if it is
58// the mangled version.
59//----------------------------------------------------------------------
60Mangled::Mangled (const ConstString &s, bool mangled) :
61 m_mangled(),
62 m_demangled()
63{
64 if (s)
65 SetValue(s, mangled);
66}
67
68//----------------------------------------------------------------------
69// Constructor with an optional string where we try and auto detect if
70// the name is mangled or not by inspecting the string value
71//----------------------------------------------------------------------
72Mangled::Mangled (const char *s) :
73 m_mangled(),
74 m_demangled()
75{
76 if (s && s[0])
77 SetValue(ConstString(s));
78}
79
80Mangled::Mangled (const ConstString &s) :
81m_mangled(),
82m_demangled()
83{
84 if (s)
85 SetValue(s);
86}
87
88//----------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +000089// Destructor
90//----------------------------------------------------------------------
91Mangled::~Mangled ()
92{
93}
94
95//----------------------------------------------------------------------
96// Convert to pointer operator. This allows code to check any Mangled
97// objects to see if they contain anything valid using code such as:
98//
99// Mangled mangled(...);
100// if (mangled)
101// { ...
102//----------------------------------------------------------------------
103Mangled::operator void* () const
104{
105 return (m_mangled) ? const_cast<Mangled*>(this) : NULL;
106}
107
108//----------------------------------------------------------------------
109// Logical NOT operator. This allows code to check any Mangled
110// objects to see if they are invalid using code such as:
111//
112// Mangled mangled(...);
113// if (!file_spec)
114// { ...
115//----------------------------------------------------------------------
116bool
117Mangled::operator! () const
118{
119 return !m_mangled;
120}
121
122//----------------------------------------------------------------------
123// Clear the mangled and demangled values.
124//----------------------------------------------------------------------
125void
126Mangled::Clear ()
127{
128 m_mangled.Clear();
129 m_demangled.Clear();
130}
131
132
133//----------------------------------------------------------------------
134// Compare the the string values.
135//----------------------------------------------------------------------
136int
137Mangled::Compare (const Mangled& a, const Mangled& b)
138{
Jim Ingham89bf5e92010-09-15 00:13:44 +0000139 return ConstString::Compare(a.GetName(ePreferMangled), a.GetName(ePreferMangled));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000140}
141
142
143
144//----------------------------------------------------------------------
145// Set the string value in this objects. If "mangled" is true, then
146// the mangled named is set with the new value in "s", else the
147// demangled name is set.
148//----------------------------------------------------------------------
149void
150Mangled::SetValue (const char *s, bool mangled)
151{
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000152 if (s)
153 {
154 if (mangled)
Greg Clayton385aa282011-04-22 03:55:06 +0000155 {
156 m_demangled.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000157 m_mangled.SetCString (s);
Greg Clayton385aa282011-04-22 03:55:06 +0000158 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000159 else
Greg Clayton385aa282011-04-22 03:55:06 +0000160 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000161 m_demangled.SetCString(s);
Greg Clayton385aa282011-04-22 03:55:06 +0000162 m_mangled.Clear();
163 }
164 }
165 else
166 {
167 m_demangled.Clear();
168 m_mangled.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000169 }
170}
171
Greg Clayton5e0c5e82012-07-18 20:47:40 +0000172void
173Mangled::SetValue (const ConstString &s, bool mangled)
174{
175 if (s)
176 {
177 if (mangled)
178 {
179 m_demangled.Clear();
180 m_mangled = s;
181 }
182 else
183 {
184 m_demangled = s;
185 m_mangled.Clear();
186 }
187 }
188 else
189 {
190 m_demangled.Clear();
191 m_mangled.Clear();
192 }
193}
194
195void
196Mangled::SetValue (const ConstString &name)
197{
198 if (name)
199 {
200 if (cstring_is_mangled(name.GetCString()))
201 {
202 m_demangled.Clear();
203 m_mangled = name;
204 }
205 else
206 {
207 m_demangled = name;
208 m_mangled.Clear();
209 }
210 }
211 else
212 {
213 m_demangled.Clear();
214 m_mangled.Clear();
215 }
216}
217
218
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000219//----------------------------------------------------------------------
220// Generate the demangled name on demand using this accessor. Code in
221// this class will need to use this accessor if it wishes to decode
222// the demangled name. The result is cached and will be kept until a
223// new string value is supplied to this object, or until the end of the
224// object's lifetime.
225//----------------------------------------------------------------------
226const ConstString&
227Mangled::GetDemangledName () const
228{
229 // Check to make sure we have a valid mangled name and that we
230 // haven't already decoded our mangled name.
231 if (m_mangled && !m_demangled)
232 {
233 // We need to generate and cache the demangled name.
234 Timer scoped_timer (__PRETTY_FUNCTION__,
235 "Mangled::GetDemangledName (m_mangled = %s)",
236 m_mangled.GetCString());
237
Greg Clayton5e0c5e82012-07-18 20:47:40 +0000238 // Don't bother running anything that isn't mangled
239 const char *mangled_cstr = m_mangled.GetCString();
240 if (cstring_is_mangled(mangled_cstr))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000241 {
Greg Claytonc3ae1ce2011-06-09 22:34:34 +0000242 if (!m_mangled.GetMangledCounterpart(m_demangled))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000243 {
Greg Claytone41e5892010-09-03 23:26:12 +0000244 // We didn't already mangle this name, demangle it and if all goes well
245 // add it to our map.
Greg Clayton5e0c5e82012-07-18 20:47:40 +0000246 char *demangled_name = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL);
Greg Claytone41e5892010-09-03 23:26:12 +0000247
248 if (demangled_name)
249 {
Greg Claytonc3ae1ce2011-06-09 22:34:34 +0000250 m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled);
Greg Claytone41e5892010-09-03 23:26:12 +0000251 free (demangled_name);
252 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000253 }
254 }
Jason Molenda3f8688b2010-12-15 04:27:04 +0000255 if (!m_demangled)
256 {
257 // Set the demangled string to the empty string to indicate we
258 // tried to parse it once and failed.
259 m_demangled.SetCString("");
260 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000261 }
262
263 return m_demangled;
264}
265
Greg Clayton83c5cd92010-11-14 22:13:40 +0000266
267bool
268Mangled::NameMatches (const RegularExpression& regex) const
269{
270 if (m_mangled && regex.Execute (m_mangled.AsCString()))
271 return true;
272
273 if (GetDemangledName() && regex.Execute (m_demangled.AsCString()))
274 return true;
275 return false;
276}
277
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000278//----------------------------------------------------------------------
279// Get the demangled name if there is one, else return the mangled name.
280//----------------------------------------------------------------------
281const ConstString&
Jim Ingham08b87e02010-09-14 22:03:00 +0000282Mangled::GetName (Mangled::NamePreference preference) const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000283{
Greg Clayton87425432010-09-14 23:44:49 +0000284 if (preference == ePreferDemangled)
Jim Ingham08b87e02010-09-14 22:03:00 +0000285 {
Greg Claytond0b89f82010-09-14 23:48:44 +0000286 // Call the accessor to make sure we get a demangled name in case
287 // it hasn't been demangled yet...
288 if (GetDemangledName())
289 return m_demangled;
Greg Clayton87425432010-09-14 23:44:49 +0000290 return m_mangled;
291 }
292 else
293 {
Greg Claytond0b89f82010-09-14 23:48:44 +0000294 if (m_mangled)
295 return m_mangled;
296 return GetDemangledName();
Jim Ingham08b87e02010-09-14 22:03:00 +0000297 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000298}
299
300//----------------------------------------------------------------------
301// Generate the tokens from the demangled name.
302//
303// Returns the number of tokens that were parsed.
304//----------------------------------------------------------------------
305size_t
306Mangled::GetTokens (Mangled::TokenList &tokens) const
307{
308 tokens.Clear();
309 const ConstString& demangled = GetDemangledName();
310 if (demangled && !demangled.IsEmpty())
311 tokens.Parse(demangled.AsCString());
312
313 return tokens.Size();
314}
315
316//----------------------------------------------------------------------
317// Dump a Mangled object to stream "s". We don't force our
318// demangled name to be computed currently (we don't use the accessor).
319//----------------------------------------------------------------------
320void
321Mangled::Dump (Stream *s) const
322{
323 if (m_mangled)
324 {
325 *s << ", mangled = " << m_mangled;
326 }
327 if (m_demangled)
328 {
329 const char * demangled = m_demangled.AsCString();
330 s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");
331 }
332}
333
334//----------------------------------------------------------------------
335// Dumps a debug version of this string with extra object and state
336// information to stream "s".
337//----------------------------------------------------------------------
338void
339Mangled::DumpDebug (Stream *s) const
340{
341 s->Printf("%*p: Mangled mangled = ", (int)sizeof(void*) * 2, this);
342 m_mangled.DumpDebug(s);
343 s->Printf(", demangled = ");
344 m_demangled.DumpDebug(s);
345}
346
347//----------------------------------------------------------------------
348// Return the size in byte that this object takes in memory. The size
349// includes the size of the objects it owns, and not the strings that
350// it references because they are shared strings.
351//----------------------------------------------------------------------
352size_t
353Mangled::MemorySize () const
354{
355 return m_mangled.MemorySize() + m_demangled.MemorySize();
356}
357
358//----------------------------------------------------------------------
359// Dump OBJ to the supplied stream S.
360//----------------------------------------------------------------------
361Stream&
362operator << (Stream& s, const Mangled& obj)
363{
364 if (obj.GetMangledName())
365 s << "mangled = '" << obj.GetMangledName() << "'";
366
367 const ConstString& demangled = obj.GetDemangledName();
368 if (demangled)
369 s << ", demangled = '" << demangled << '\'';
370 else
371 s << ", demangled = <error>";
372 return s;
373}
374
375
376
377
378#pragma mark Mangled::Token
379
380//--------------------------------------------------------------
381// Default constructor
382//--------------------------------------------------------------
383Mangled::Token::Token () :
384 type(eInvalid),
385 value()
386{
387}
388
389//--------------------------------------------------------------
390// Equal to operator
391//--------------------------------------------------------------
392bool
393Mangled::Token::operator== (const Token& rhs) const
394{
395 return type == rhs.type && value == rhs.value;
396}
397
398//--------------------------------------------------------------
399// Dump the token to a stream "s"
400//--------------------------------------------------------------
401void
402Mangled::Token::Dump (Stream *s) const
403{
404 switch (type)
405 {
406 case eInvalid: s->PutCString("invalid "); break;
407 case eNameSpace: s->PutCString("namespace "); break;
408 case eMethodName: s->PutCString("method "); break;
409 case eType: s->PutCString("type "); break;
410 case eTemplate: s->PutCString("template "); break;
411 case eTemplateBeg: s->PutCString("template < "); break;
412 case eTemplateEnd: s->PutCString("template > "); break;
413 case eParamsBeg: s->PutCString("params ( "); break;
414 case eParamsEnd: s->PutCString("params ) "); break;
415 case eQualifier: s->PutCString("qualifier "); break;
416 case eError: s->PutCString("ERROR "); break;
417 default:
418 s->Printf("type = %i", type);
419 break;
420 }
421 value.DumpDebug(s);
422}
423
424//--------------------------------------------------------------
425// Returns true if this token is a wildcard
426//--------------------------------------------------------------
427bool
428Mangled::Token::IsWildcard () const
429{
430 static ConstString g_wildcard_str("*");
431 return value == g_wildcard_str;
432}
433
434
435//----------------------------------------------------------------------
436// Dump "obj" to the supplied stream "s"
437//----------------------------------------------------------------------
438Stream&
439lldb_private::operator << (Stream& s, const Mangled::Token& obj)
440{
441 obj.Dump(&s);
442 return s;
443}
444
445
446#pragma mark Mangled::TokenList
447//----------------------------------------------------------------------
448// Mangled::TokenList
449//----------------------------------------------------------------------
450
451//--------------------------------------------------------------
452// Default constructor. If demangled is non-NULL and not-empty
453// the token list will parse up the demangled string it is
454// given, else the object will initialize an empty token list.
455//--------------------------------------------------------------
456Mangled::TokenList::TokenList (const char *demangled) :
457 m_tokens()
458{
459 if (demangled && demangled[0])
460 {
461 Parse(demangled);
462 }
463}
464
465//----------------------------------------------------------------------
466// Destructor
467//----------------------------------------------------------------------
468Mangled::TokenList::~TokenList ()
469{
470}
471
472//----------------------------------------------------------------------
473// Parses "demangled" into tokens. This allows complex
474// comparisons to be done. Comparisons can include wildcards at
475// the namespace, method name, template, and template and
476// parameter type levels.
477//
478// Example queries include:
479// "std::basic_string<*>" // Find all std::basic_string variants
480// "std::basic_string<*>::erase(*)" // Find all std::basic_string::erase variants with any number of parameters
481// "*::clear()" // Find all functions with a method name of
482// // "clear" that are in any namespace that
483// // have no parameters
484// "::printf" // Find the printf function in the global namespace
485// "printf" // Ditto
486// "foo::*(int)" // Find all functions in the class or namespace "foo" that take a single integer argument
487//
488// Returns the number of tokens that were decoded, or zero when
489// we fail.
490//----------------------------------------------------------------------
491size_t
492Mangled::TokenList::Parse (const char *s)
493{
494 m_tokens.clear();
495
496 Token token;
497 token.type = eNameSpace;
498
499 TokenType max_type = eInvalid;
500 const char *p = s;
501 size_t span = 0;
502 size_t sep_size = 0;
503
504 while (*p != '\0')
505 {
506 p = p + span + sep_size;
507 while (isspace(*p))
508 ++p;
509
510 if (*p == '\0')
511 break;
512
513 span = strcspn(p, ":<>(),");
514 sep_size = 1;
515 token.type = eInvalid;
516 switch (p[span])
517 {
518 case '\0':
519 break;
520
521 case ':':
522 if (p[span+1] == ':')
523 {
524 sep_size = 2;
525 if (span > 0)
526 {
527 token.type = eNameSpace;
528 token.value.SetCStringWithLength (p, span);
529 m_tokens.push_back(token);
530 }
531 else
532 continue;
533 }
534 break;
535
536 case '(':
537 if (span > 0)
538 {
539 token.type = eMethodName;
540 token.value.SetCStringWithLength (p, span);
541 m_tokens.push_back(token);
542 }
543
544 token.type = eParamsBeg;
545 token.value.Clear();
546 m_tokens.push_back(token);
547 break;
548
549 case ',':
550 if (span > 0)
551 {
552 token.type = eType;
553 token.value.SetCStringWithLength (p, span);
554 m_tokens.push_back(token);
555 }
556 else
557 {
558 continue;
559 }
560 break;
561
562 case ')':
563 if (span > 0)
564 {
565 token.type = eType;
566 token.value.SetCStringWithLength (p, span);
567 m_tokens.push_back(token);
568 }
569
570 token.type = eParamsEnd;
571 token.value.Clear();
572 m_tokens.push_back(token);
573 break;
574
575 case '<':
576 if (span > 0)
577 {
578 token.type = eTemplate;
579 token.value.SetCStringWithLength (p, span);
580 m_tokens.push_back(token);
581 }
582
583 token.type = eTemplateBeg;
584 token.value.Clear();
585 m_tokens.push_back(token);
586 break;
587
588 case '>':
589 if (span > 0)
590 {
591 token.type = eType;
592 token.value.SetCStringWithLength (p, span);
593 m_tokens.push_back(token);
594 }
595
596 token.type = eTemplateEnd;
597 token.value.Clear();
598 m_tokens.push_back(token);
599 break;
600 }
601
602 if (max_type < token.type)
603 max_type = token.type;
604
605 if (token.type == eInvalid)
606 {
607 if (max_type >= eParamsEnd)
608 {
609 token.type = eQualifier;
610 token.value.SetCString(p);
611 m_tokens.push_back(token);
612 }
613 else if (max_type >= eParamsBeg)
614 {
615 token.type = eType;
616 token.value.SetCString(p);
617 m_tokens.push_back(token);
618 }
619 else
620 {
621 token.type = eMethodName;
622 token.value.SetCString(p);
623 m_tokens.push_back(token);
624 }
625 break;
626 }
627 }
628 return m_tokens.size();
629}
630
631
632//----------------------------------------------------------------------
633// Clear the token list.
634//----------------------------------------------------------------------
635void
636Mangled::TokenList::Clear ()
637{
638 m_tokens.clear();
639}
640
641//----------------------------------------------------------------------
642// Dump the token list to the stream "s"
643//----------------------------------------------------------------------
644void
645Mangled::TokenList::Dump (Stream *s) const
646{
647 collection::const_iterator pos;
648 collection::const_iterator beg = m_tokens.begin();
649 collection::const_iterator end = m_tokens.end();
650 for (pos = beg; pos != end; ++pos)
651 {
652 s->Indent("token[");
653 *s << (uint32_t)std::distance(beg, pos) << "] = " << *pos << "\n";
654 }
655}
656
657//----------------------------------------------------------------------
658// Find the first token in the list that has "token_type" as its
659// type
660//----------------------------------------------------------------------
661const Mangled::Token *
662Mangled::TokenList::Find (TokenType token_type) const
663{
664 collection::const_iterator pos;
665 collection::const_iterator beg = m_tokens.begin();
666 collection::const_iterator end = m_tokens.end();
667 for (pos = beg; pos != end; ++pos)
668 {
669 if (pos->type == token_type)
670 return &(*pos);
671 }
672 return NULL;
673}
674
675//----------------------------------------------------------------------
676// Return the token at index "idx", or NULL if the index is
677// out of range.
678//----------------------------------------------------------------------
679const Mangled::Token *
680Mangled::TokenList::GetTokenAtIndex (uint32_t idx) const
681{
682 if (idx < m_tokens.size())
683 return &m_tokens[idx];
684 return NULL;
685}
686
687
688//----------------------------------------------------------------------
689// Given a token list, see if it matches this object's tokens.
690// "token_list" can contain wild card values to enable powerful
691// matching. Matching the std::string::erase(*) example that was
692// tokenized above we could use a token list such as:
693//
694// token name
695// ----------- ----------------------------------------
696// eNameSpace "std"
697// eTemplate "basic_string"
698// eTemplateBeg
699// eInvalid "*"
700// eTemplateEnd
701// eMethodName "erase"
702// eParamsBeg
703// eInvalid "*"
704// eParamsEnd
705//
706// Returns true if it "token_list" matches this object's tokens,
707// false otherwise.
708//----------------------------------------------------------------------
709bool
710Mangled::TokenList::MatchesQuery (const Mangled::TokenList &match) const
711{
712 size_t match_count = 0;
713 collection::const_iterator pos;
714 collection::const_iterator pos_end = m_tokens.end();
715
716 collection::const_iterator match_pos;
717 collection::const_iterator match_pos_end = match.m_tokens.end();
718 collection::const_iterator match_wildcard_pos = match_pos_end;
719 collection::const_iterator match_next_pos = match_pos_end;
720
721 size_t template_scope_depth = 0;
722
723 for (pos = m_tokens.begin(), match_pos = match.m_tokens.begin();
724 pos != pos_end && match_pos != match_pos_end;
725 ++match_pos)
726 {
727 match_next_pos = match_pos + 1;
728 // Is this a wildcard?
729 if (match_pos->IsWildcard())
730 {
731 if (match_wildcard_pos != match_pos_end)
732 return false; // Can't have two wildcards in effect at once.
733
734 match_wildcard_pos = match_pos;
735 // Are we at the end of the MATCH token list?
736 if (match_next_pos == match_pos_end)
737 {
738 // There is nothing more to match, return if we have any matches so far...
739 return match_count > 0;
740 }
741 }
742
743 if (match_pos->type == eInvalid || match_pos->type == eError)
744 {
745 return false;
746 }
747 else
748 {
749 if (match_pos->type == eTemplateBeg)
750 {
751 ++template_scope_depth;
752 }
753 else if (match_pos->type == eTemplateEnd)
754 {
755 assert(template_scope_depth > 0);
756 --template_scope_depth;
757 }
758
759 // Do we have a wildcard going right now?
760 if (match_wildcard_pos == match_pos_end)
761 {
762 // No wildcard matching right now, just check and see if things match
763 if (*pos == *match_pos)
764 ++match_count;
765 else
766 return false;
767 }
768 else
769 {
770 // We have a wildcard match going
771
772 // For template types we need to make sure to match the template depths...
773 const size_t start_wildcard_template_scope_depth = template_scope_depth;
774 size_t curr_wildcard_template_scope_depth = template_scope_depth;
775 while (pos != pos_end)
776 {
777 if (match_wildcard_pos->type == eNameSpace && pos->type == eParamsBeg)
778 return false;
779
780 if (start_wildcard_template_scope_depth == curr_wildcard_template_scope_depth)
781 {
782 if (*pos == *match_next_pos)
783 {
784 ++match_count;
785 match_pos = match_next_pos;
786 match_wildcard_pos = match_pos_end;
787 break;
788 }
789 }
790 if (pos->type == eTemplateBeg)
791 ++curr_wildcard_template_scope_depth;
792 else if (pos->type == eTemplateEnd)
793 --curr_wildcard_template_scope_depth;
794
795
796 ++pos;
797 }
798 }
799 }
800
801 if (pos != pos_end)
802 ++pos;
803 }
804 if (match_pos != match_pos_end)
805 return false;
806
807 return match_count > 0;
808}
809
810
811//----------------------------------------------------------------------
812// Return the number of tokens in the token collection
813//----------------------------------------------------------------------
814size_t
815Mangled::TokenList::Size () const
816{
817 return m_tokens.size();
818}
819
820
821//----------------------------------------------------------------------
822// Stream out the tokens
823//----------------------------------------------------------------------
824Stream&
825lldb_private::operator << (Stream& s, const Mangled::TokenList& obj)
826{
827 obj.Dump(&s);
828 return s;
829}