// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package demangle defines functions that demangle GCC/LLVM C++ symbol names. // This package recognizes names that were mangled according to the C++ ABI // defined at http://codesourcery.com/cxx-abi/. package demangle import ( "errors" "fmt" "strings" ) // ErrNotMangledName is returned by CheckedDemangle if the string does // not appear to be a C++ symbol name. var ErrNotMangledName = errors.New("not a C++ mangled name") // Option is the type of demangler options. type Option int const ( // The NoParams option disables demangling of function parameters. NoParams Option = iota // The NoTemplateParams option disables demangling of template parameters. NoTemplateParams // The NoClones option disables inclusion of clone suffixes. // NoParams implies NoClones. NoClones // The Verbose option turns on more verbose demangling. Verbose ) // Filter demangles a C++ symbol name, returning the human-readable C++ name. // If any error occurs during demangling, the input string is returned. func Filter(name string, options ...Option) string { ret, err := ToString(name, options...) if err != nil { return name } return ret } // ToString demangles a C++ symbol name, returning human-readable C++ // name or an error. // If the name does not appear to be a C++ symbol name at all, the // error will be ErrNotMangledName. func ToString(name string, options ...Option) (string, error) { a, err := ToAST(name, options...) if err != nil { return "", err } return ASTToString(a, options...), nil } // ToAST demangles a C++ symbol name into an abstract syntax tree // representing the symbol. // If the NoParams option is passed, and the name has a function type, // the parameter types are not demangled. // If the name does not appear to be a C++ symbol name at all, the // error will be ErrNotMangledName. func ToAST(name string, options ...Option) (AST, error) { if strings.HasPrefix(name, "_Z") { a, err := doDemangle(name[2:], options...) return a, adjustErr(err, 2) } const prefix = "_GLOBAL_" if strings.HasPrefix(name, prefix) { // The standard demangler ignores NoParams for global // constructors. We are compatible. i := 0 for i < len(options) { if options[i] == NoParams { options = append(options[:i], options[i+1:]...) } else { i++ } } a, err := globalCDtorName(name[len(prefix):], options...) return a, adjustErr(err, len(prefix)) } return nil, ErrNotMangledName } // globalCDtorName demangles a global constructor/destructor symbol name. // The parameter is the string following the "_GLOBAL_" prefix. func globalCDtorName(name string, options ...Option) (AST, error) { if len(name) < 4 { return nil, ErrNotMangledName } switch name[0] { case '.', '_', '$': default: return nil, ErrNotMangledName } var ctor bool switch name[1] { case 'I': ctor = true case 'D': ctor = false default: return nil, ErrNotMangledName } if name[2] != '_' { return nil, ErrNotMangledName } if !strings.HasPrefix(name[3:], "_Z") { return &GlobalCDtor{Ctor: ctor, Key: &Name{Name: name}}, nil } else { a, err := doDemangle(name[5:], options...) if err != nil { return nil, adjustErr(err, 5) } return &GlobalCDtor{Ctor: ctor, Key: a}, nil } } // The doDemangle function is the entry point into the demangler proper. func doDemangle(name string, options ...Option) (ret AST, err error) { // When the demangling routines encounter an error, they panic // with a value of type demangleErr. defer func() { if r := recover(); r != nil { if de, ok := r.(demangleErr); ok { ret = nil err = de return } panic(r) } }() params := true clones := true verbose := false for _, o := range options { switch o { case NoParams: params = false clones = false case NoTemplateParams: // This is a valid option but only affect printing of the AST. case NoClones: clones = false case Verbose: verbose = true default: return nil, fmt.Errorf("unrecognized demangler option %v", o) } } st := &state{str: name, verbose: verbose} a := st.encoding(params, notForLocalName) // Accept a clone suffix. if clones { for len(st.str) > 1 && st.str[0] == '.' && (isLower(st.str[1]) || st.str[1] == '_' || isDigit(st.str[1])) { a = st.cloneSuffix(a) } } if clones && len(st.str) > 0 { st.fail("unparsed characters at end of mangled name") } return a, nil } // A state holds the current state of demangling a string. type state struct { str string // remainder of string to demangle verbose bool // whether to use verbose demangling off int // offset of str within original string subs substitutions // substitutions templates []*Template // templates being processed } // copy returns a copy of the current state. func (st *state) copy() *state { n := new(state) *n = *st return n } // fail panics with demangleErr, to be caught in doDemangle. func (st *state) fail(err string) { panic(demangleErr{err: err, off: st.off}) } // failEarlier is like fail, but decrements the offset to indicate // that the point of failure occurred earlier in the string. func (st *state) failEarlier(err string, dec int) { if st.off < dec { panic("internal error") } panic(demangleErr{err: err, off: st.off - dec}) } // advance advances the current string offset. func (st *state) advance(add int) { if len(st.str) < add { panic("internal error") } st.str = st.str[add:] st.off += add } // checkChar requires that the next character in the string be c, and // advances past it. func (st *state) checkChar(c byte) { if len(st.str) == 0 || st.str[0] != c { panic("internal error") } st.advance(1) } // A demangleErr is an error at a specific offset in the mangled // string. type demangleErr struct { err string off int } // Error implements the builtin error interface for demangleErr. func (de demangleErr) Error() string { return fmt.Sprintf("%s at %d", de.err, de.off) } // adjustErr adjusts the position of err, if it is a demangleErr, // and returns err. func adjustErr(err error, adj int) error { if err == nil { return nil } if de, ok := err.(demangleErr); ok { de.off += adj return de } return err } type forLocalNameType int const ( forLocalName forLocalNameType = iota notForLocalName ) // encoding ::= <(function) name> // <(data) name> // func (st *state) encoding(params bool, local forLocalNameType) AST { if len(st.str) < 1 { st.fail("expected encoding") } if st.str[0] == 'G' || st.str[0] == 'T' { return st.specialName() } a := st.name() a = simplify(a) if !params { // Don't demangle the parameters. // Strip CV-qualifiers, as they apply to the 'this' // parameter, and are not output by the standard // demangler without parameters. if mwq, ok := a.(*MethodWithQualifiers); ok { a = mwq.Method } // If this is a local name, there may be CV-qualifiers // on the name that really apply to the top level, and // therefore must be discarded when discarding // parameters. This can happen when parsing a class // that is local to a function. if q, ok := a.(*Qualified); ok && q.LocalName { p := &q.Name if da, ok := (*p).(*DefaultArg); ok { p = &da.Arg } if mwq, ok := (*p).(*MethodWithQualifiers); ok { *p = mwq.Method } } return a } if len(st.str) == 0 || st.str[0] == 'E' { // There are no parameters--this is a data symbol, not // a function symbol. return a } check := a mwq, _ := check.(*MethodWithQualifiers) if mwq != nil { check = mwq.Method } template, _ := check.(*Template) if template != nil { st.templates = append(st.templates, template) } ft := st.bareFunctionType(hasReturnType(a)) if template != nil { st.templates = st.templates[:len(st.templates)-1] } ft = simplify(ft) // For a local name, discard the return type, so that it // doesn't get confused with the top level return type. if local == forLocalName { if functype, ok := ft.(*FunctionType); ok { functype.Return = nil } } // Any top-level qualifiers belong to the function type. if mwq != nil { a = mwq.Method mwq.Method = ft ft = mwq } if q, ok := a.(*Qualified); ok && q.LocalName { p := &q.Name if da, ok := (*p).(*DefaultArg); ok { p = &da.Arg } if mwq, ok := (*p).(*MethodWithQualifiers); ok { *p = mwq.Method mwq.Method = ft ft = mwq } } return &Typed{Name: a, Type: ft} } // hasReturnType returns whether the mangled form of a will have a // return type. func hasReturnType(a AST) bool { switch a := a.(type) { case *Template: return !isCDtorConversion(a.Name) case *TypeWithQualifiers: return hasReturnType(a.Base) case *MethodWithQualifiers: return hasReturnType(a.Method) default: return false } } // isCDtorConversion returns when an AST is a constructor, a // destructor, or a conversion operator. func isCDtorConversion(a AST) bool { switch a := a.(type) { case *Qualified: return isCDtorConversion(a.Name) case *Constructor, *Destructor, *Cast: return true default: return false } } // ::= B func (st *state) taggedName(a AST) AST { for len(st.str) > 0 && st.str[0] == 'B' { st.advance(1) tag := st.sourceName() a = &TaggedName{Name: a, Tag: tag} } return a } // ::= // ::= // ::= // ::= // // ::= // ::= St // // ::= // ::= func (st *state) name() AST { if len(st.str) < 1 { st.fail("expected name") } switch st.str[0] { case 'N': return st.nestedName() case 'Z': return st.localName() case 'U': a, isCast := st.unqualifiedName() if isCast { st.setTemplate(a, nil) } return a case 'S': if len(st.str) < 2 { st.advance(1) st.fail("expected substitution index") } var a AST isCast := false subst := false if st.str[1] == 't' { st.advance(2) a, isCast = st.unqualifiedName() a = &Qualified{Scope: &Name{Name: "std"}, Name: a, LocalName: false} } else { a = st.substitution(false) subst = true } if len(st.str) > 0 && st.str[0] == 'I' { // This can only happen if we saw // and are about to see // . is a // substitution candidate if it did not come from a // substitution. if !subst { st.subs.add(a) } args := st.templateArgs() tmpl := &Template{Name: a, Args: args} if isCast { st.setTemplate(a, tmpl) st.clearTemplateArgs(args) isCast = false } a = tmpl } if isCast { st.setTemplate(a, nil) } return a default: a, isCast := st.unqualifiedName() if len(st.str) > 0 && st.str[0] == 'I' { st.subs.add(a) args := st.templateArgs() tmpl := &Template{Name: a, Args: args} if isCast { st.setTemplate(a, tmpl) st.clearTemplateArgs(args) isCast = false } a = tmpl } if isCast { st.setTemplate(a, nil) } return a } } // ::= N [] [] E // ::= N [] [] E func (st *state) nestedName() AST { st.checkChar('N') q := st.cvQualifiers() r := st.refQualifier() a := st.prefix() if len(q) > 0 || r != "" { a = &MethodWithQualifiers{Method: a, Qualifiers: q, RefQualifier: r} } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after nested name") } st.advance(1) return a } // ::= // ::= // ::= // ::= // ::= // ::= // // ::= <(template) unqualified-name> // ::= // ::= // // ::= Dt E // ::= DT E func (st *state) prefix() AST { var a AST // The last name seen, for a constructor/destructor. var last AST getLast := func(a AST) AST { for { if t, ok := a.(*Template); ok { a = t.Name } else if q, ok := a.(*Qualified); ok { a = q.Name } else if t, ok := a.(*TaggedName); ok { a = t.Name } else { return a } } } isCast := false for { if len(st.str) == 0 { st.fail("expected prefix") } var next AST c := st.str[0] if isDigit(c) || isLower(c) || c == 'U' || c == 'L' { un, isUnCast := st.unqualifiedName() next = un if isUnCast { isCast = true } } else { switch st.str[0] { case 'C': inheriting := false st.advance(1) if len(st.str) > 0 && st.str[0] == 'I' { inheriting = true st.advance(1) } if len(st.str) < 1 { st.fail("expected constructor type") } if last == nil { st.fail("constructor before name is seen") } st.advance(1) if inheriting { last = st.demangleType(false) } next = &Constructor{Name: getLast(last)} case 'D': if len(st.str) > 1 && (st.str[1] == 'T' || st.str[1] == 't') { next = st.demangleType(false) } else { if len(st.str) < 2 { st.fail("expected destructor type") } if last == nil { st.fail("destructor before name is seen") } st.advance(2) next = &Destructor{Name: getLast(last)} } case 'S': next = st.substitution(true) case 'I': if a == nil { st.fail("unexpected template arguments") } var args []AST args = st.templateArgs() tmpl := &Template{Name: a, Args: args} if isCast { st.setTemplate(a, tmpl) st.clearTemplateArgs(args) isCast = false } a = nil next = tmpl case 'T': next = st.templateParam() case 'E': if a == nil { st.fail("expected prefix") } if isCast { st.setTemplate(a, nil) } return a case 'M': if a == nil { st.fail("unexpected lambda initializer") } // This is the initializer scope for a // lambda. We don't need to record // it. The normal code will treat the // variable has a type scope, which // gives appropriate output. st.advance(1) continue default: st.fail("unrecognized letter in prefix") } } last = next if a == nil { a = next } else { a = &Qualified{Scope: a, Name: next, LocalName: false} } if c != 'S' && (len(st.str) == 0 || st.str[0] != 'E') { st.subs.add(a) } } } // ::= // ::= // ::= // ::= // // ::= L func (st *state) unqualifiedName() (r AST, isCast bool) { if len(st.str) < 1 { st.fail("expected unqualified name") } var a AST isCast = false c := st.str[0] if isDigit(c) { a = st.sourceName() } else if isLower(c) { a, _ = st.operatorName(false) if _, ok := a.(*Cast); ok { isCast = true } if op, ok := a.(*Operator); ok && op.Name == `operator"" ` { n := st.sourceName() a = &Unary{Op: op, Expr: n, Suffix: false, SizeofType: false} } } else { switch c { case 'C', 'D': st.fail("constructor/destructor not in nested name") case 'L': st.advance(1) a = st.sourceName() a = st.discriminator(a) case 'U': if len(st.str) < 2 { st.advance(1) st.fail("expected closure or unnamed type") } c := st.str[1] switch c { case 'l': a = st.closureTypeName() case 't': a = st.unnamedTypeName() default: st.advance(1) st.fail("expected closure or unnamed type") } default: st.fail("expected unqualified name") } } if len(st.str) > 0 && st.str[0] == 'B' { a = st.taggedName(a) } return a, isCast } // ::= <(positive length) number> // identifier ::= <(unqualified source code identifier)> func (st *state) sourceName() AST { val := st.number() if val <= 0 { st.fail("expected positive number") } if len(st.str) < val { st.fail("not enough characters for identifier") } id := st.str[:val] st.advance(val) // Look for GCC encoding of anonymous namespace, and make it // more friendly. const anonPrefix = "_GLOBAL_" if strings.HasPrefix(id, anonPrefix) && len(id) > len(anonPrefix)+2 { c1 := id[len(anonPrefix)] c2 := id[len(anonPrefix)+1] if (c1 == '.' || c1 == '_' || c1 == '$') && c2 == 'N' { id = "(anonymous namespace)" } } n := &Name{Name: id} return n } // number ::= [n] <(non-negative decimal integer)> func (st *state) number() int { neg := false if len(st.str) > 0 && st.str[0] == 'n' { neg = true st.advance(1) } if len(st.str) == 0 || !isDigit(st.str[0]) { st.fail("missing number") } val := 0 for len(st.str) > 0 && isDigit(st.str[0]) { // Number picked to ensure we can't overflow with 32-bit int. // Any very large number here is bogus. if val >= 0x80000000/10-10 { st.fail("numeric overflow") } val = val*10 + int(st.str[0]-'0') st.advance(1) } if neg { val = -val } return val } // An operator is the demangled name, and the number of arguments it // takes in an expression. type operator struct { name string args int } // The operators map maps the mangled operator names to information // about them. var operators = map[string]operator{ "aN": {"&=", 2}, "aS": {"=", 2}, "aa": {"&&", 2}, "ad": {"&", 1}, "an": {"&", 2}, "at": {"alignof ", 1}, "az": {"alignof ", 1}, "cc": {"const_cast", 2}, "cl": {"()", 2}, "cm": {",", 2}, "co": {"~", 1}, "dV": {"/=", 2}, "da": {"delete[] ", 1}, "dc": {"dynamic_cast", 2}, "de": {"*", 1}, "dl": {"delete ", 1}, "ds": {".*", 2}, "dt": {".", 2}, "dv": {"/", 2}, "eO": {"^=", 2}, "eo": {"^", 2}, "eq": {"==", 2}, "fl": {"...", 2}, "fr": {"...", 2}, "fL": {"...", 3}, "fR": {"...", 3}, "ge": {">=", 2}, "gs": {"::", 1}, "gt": {">", 2}, "ix": {"[]", 2}, "lS": {"<<=", 2}, "le": {"<=", 2}, "li": {`operator"" `, 1}, "ls": {"<<", 2}, "lt": {"<", 2}, "mI": {"-=", 2}, "mL": {"*=", 2}, "mi": {"-", 2}, "ml": {"*", 2}, "mm": {"--", 1}, "na": {"new[]", 3}, "ne": {"!=", 2}, "ng": {"-", 1}, "nt": {"!", 1}, "nw": {"new", 3}, "oR": {"|=", 2}, "oo": {"||", 2}, "or": {"|", 2}, "pL": {"+=", 2}, "pl": {"+", 2}, "pm": {"->*", 2}, "pp": {"++", 1}, "ps": {"+", 1}, "pt": {"->", 2}, "qu": {"?", 3}, "rM": {"%=", 2}, "rS": {">>=", 2}, "rc": {"reinterpret_cast", 2}, "rm": {"%", 2}, "rs": {">>", 2}, "sc": {"static_cast", 2}, "st": {"sizeof ", 1}, "sz": {"sizeof ", 1}, "tr": {"throw", 0}, "tw": {"throw ", 1}, } // operator_name ::= many different two character encodings. // ::= cv // ::= v // // We need to know whether we are in an expression because it affects // how we handle template parameters in the type of a cast operator. func (st *state) operatorName(inExpression bool) (AST, int) { if len(st.str) < 2 { st.fail("missing operator code") } code := st.str[:2] st.advance(2) if code[0] == 'v' && isDigit(code[1]) { name := st.sourceName() return &Operator{Name: name.(*Name).Name}, int(code[1] - '0') } else if code == "cv" { // Push a nil on templates to indicate that template // parameters will have their template filled in // later. if !inExpression { st.templates = append(st.templates, nil) } t := st.demangleType(!inExpression) if !inExpression { st.templates = st.templates[:len(st.templates)-1] } return &Cast{To: t}, 1 } else if op, ok := operators[code]; ok { return &Operator{Name: op.name}, op.args } else { st.failEarlier("unrecognized operator code", 2) panic("not reached") } } // ::= Z <(function) encoding> E <(entity) name> [] // ::= Z <(function) encoding> E s [] // ::= Z <(function) encoding> E d [ number>] _ func (st *state) localName() AST { st.checkChar('Z') fn := st.encoding(true, forLocalName) if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after local name") } st.advance(1) if len(st.str) > 0 && st.str[0] == 's' { st.advance(1) var n AST = &Name{Name: "string literal"} n = st.discriminator(n) return &Qualified{Scope: fn, Name: n, LocalName: true} } else { num := -1 if len(st.str) > 0 && st.str[0] == 'd' { // Default argument scope. st.advance(1) num = st.compactNumber() } n := st.name() n = st.discriminator(n) if num >= 0 { n = &DefaultArg{Num: num, Arg: n} } return &Qualified{Scope: fn, Name: n, LocalName: true} } } // Parse a Java resource special-name. func (st *state) javaResource() AST { off := st.off ln := st.number() if ln <= 1 { st.failEarlier("java resource length less than 1", st.off-off) } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after number") } st.advance(1) ln-- if len(st.str) < ln { st.fail("not enough characters for java resource length") } str := st.str[:ln] final := "" st.advance(ln) for i := 0; i < len(str); i++ { if str[i] != '$' { final += string(str[i]) } else { if len(str) <= i+1 { st.failEarlier("java resource escape at end of string", 1) } i++ r, ok := map[byte]string{ 'S': "/", '_': ".", '$': "$", }[str[i]] if !ok { st.failEarlier("unrecognized java resource escape", ln-i-1) } final += r } } return &Special{Prefix: "java resource ", Val: &Name{Name: final}} } // ::= TV // ::= TT // ::= TI // ::= TS // ::= GV <(object) name> // ::= T <(base) encoding> // ::= Tc <(base) encoding> // Also g++ extensions: // ::= TC <(offset) number> _ <(base) type> // ::= TF // ::= TJ // ::= GR // ::= GA // ::= Gr // ::= GTt // ::= GTn func (st *state) specialName() AST { if st.str[0] == 'T' { st.advance(1) if len(st.str) == 0 { st.fail("expected special name code") } c := st.str[0] st.advance(1) switch c { case 'V': t := st.demangleType(false) return &Special{Prefix: "vtable for ", Val: t} case 'T': t := st.demangleType(false) return &Special{Prefix: "VTT for ", Val: t} case 'I': t := st.demangleType(false) return &Special{Prefix: "typeinfo for ", Val: t} case 'S': t := st.demangleType(false) return &Special{Prefix: "typeinfo name for ", Val: t} case 'h': st.callOffset('h') v := st.encoding(true, notForLocalName) return &Special{Prefix: "non-virtual thunk to ", Val: v} case 'v': st.callOffset('v') v := st.encoding(true, notForLocalName) return &Special{Prefix: "virtual thunk to ", Val: v} case 'c': st.callOffset(0) st.callOffset(0) v := st.encoding(true, notForLocalName) return &Special{Prefix: "covariant return thunk to ", Val: v} case 'C': derived := st.demangleType(false) off := st.off offset := st.number() if offset < 0 { st.failEarlier("expected positive offset", st.off-off) } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after number") } st.advance(1) base := st.demangleType(false) return &Special2{Prefix: "construction vtable for ", Val1: base, Middle: "-in-", Val2: derived} case 'F': t := st.demangleType(false) return &Special{Prefix: "typeinfo fn for ", Val: t} case 'J': t := st.demangleType(false) return &Special{Prefix: "java Class for ", Val: t} case 'H': n := st.name() return &Special{Prefix: "TLS init function for ", Val: n} case 'W': n := st.name() return &Special{Prefix: "TLS wrapper function for ", Val: n} default: st.fail("unrecognized special T name code") panic("not reached") } } else { st.checkChar('G') if len(st.str) == 0 { st.fail("expected special name code") } c := st.str[0] st.advance(1) switch c { case 'V': n := st.name() return &Special{Prefix: "guard variable for ", Val: n} case 'R': n := st.name() i := st.number() return &Special{Prefix: fmt.Sprintf("reference temporary #%d for ", i), Val: n} case 'A': v := st.encoding(true, notForLocalName) return &Special{Prefix: "hidden alias for ", Val: v} case 'T': if len(st.str) == 0 { st.fail("expected special GT name code") } c := st.str[0] st.advance(1) v := st.encoding(true, notForLocalName) switch c { case 'n': return &Special{Prefix: "non-transaction clone for ", Val: v} default: // The proposal is that different // letters stand for different types // of transactional cloning. Treat // them all the same for now. fallthrough case 't': return &Special{Prefix: "transaction clone for ", Val: v} } case 'r': return st.javaResource() default: st.fail("unrecognized special G name code") panic("not reached") } } } // ::= h _ // ::= v _ // // ::= <(offset) number> // // ::= <(offset) number> _ <(virtual offset) number> // // The c parameter, if not 0, is a character we just read which is the // start of the . // // We don't display the offset information anywhere. func (st *state) callOffset(c byte) { if c == 0 { if len(st.str) == 0 { st.fail("missing call offset") } c = st.str[0] st.advance(1) } switch c { case 'h': st.number() case 'v': st.number() if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after number") } st.advance(1) st.number() default: st.failEarlier("unrecognized call offset code", 1) } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after call offset") } st.advance(1) } // builtinTypes maps the type letter to the type name. var builtinTypes = map[byte]string{ 'a': "signed char", 'b': "bool", 'c': "char", 'd': "double", 'e': "long double", 'f': "float", 'g': "__float128", 'h': "unsigned char", 'i': "int", 'j': "unsigned int", 'l': "long", 'm': "unsigned long", 'n': "__int128", 'o': "unsigned __int128", 's': "short", 't': "unsigned short", 'v': "void", 'w': "wchar_t", 'x': "long long", 'y': "unsigned long long", 'z': "...", } // ::= // ::= // ::= // ::= // ::= // ::= // ::= // ::= // ::= // ::= P // ::= R // ::= O (C++0x) // ::= C // ::= G // ::= U // // ::= various one letter codes // ::= u func (st *state) demangleType(isCast bool) AST { if len(st.str) == 0 { st.fail("expected type") } addSubst := true q := st.cvQualifiers() if len(q) > 0 { if len(st.str) == 0 { st.fail("expected type") } // CV-qualifiers before a function type apply to // 'this', so avoid adding the unqualified function // type to the substitution list. if st.str[0] == 'F' { addSubst = false } } var ret AST // Use correct substitution for a template parameter. var sub AST if btype, ok := builtinTypes[st.str[0]]; ok { ret = &BuiltinType{Name: btype} st.advance(1) if len(q) > 0 { ret = &TypeWithQualifiers{Base: ret, Qualifiers: q} st.subs.add(ret) } return ret } c := st.str[0] switch c { case 'u': st.advance(1) ret = st.sourceName() case 'F': ret = st.functionType() case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': ret = st.name() case 'A': ret = st.arrayType(isCast) case 'M': ret = st.pointerToMemberType(isCast) case 'T': ret = st.templateParam() if len(st.str) > 0 && st.str[0] == 'I' { // See the function comment to explain this. if !isCast { st.subs.add(ret) args := st.templateArgs() ret = &Template{Name: ret, Args: args} } else { ret = st.demangleCastTemplateArgs(ret, true) } } case 'S': // If this is a special substitution, then it // is the start of . var c2 byte if len(st.str) > 1 { c2 = st.str[1] } if isDigit(c2) || c2 == '_' || isUpper(c2) { ret = st.substitution(false) if len(st.str) == 0 || st.str[0] != 'I' { addSubst = false } else { // See the function comment to explain this. if _, ok := ret.(*TemplateParam); !ok || !isCast { args := st.templateArgs() ret = &Template{Name: ret, Args: args} } else { next := st.demangleCastTemplateArgs(ret, false) if next == ret { addSubst = false } ret = next } } } else { ret = st.name() // This substitution is not itself a // substitution candidate, unless template // arguments were added. if ret == subAST[c2] || ret == verboseAST[c2] { addSubst = false } } case 'O', 'P', 'R', 'C', 'G': st.advance(1) t := st.demangleType(isCast) switch c { case 'O': ret = &RvalueReferenceType{Base: t} case 'P': ret = &PointerType{Base: t} case 'R': ret = &ReferenceType{Base: t} case 'C': ret = &ComplexType{Base: t} case 'G': ret = &ImaginaryType{Base: t} } case 'U': if len(st.str) < 2 { st.fail("expected source name or unnamed type") } switch st.str[1] { case 'l': ret = st.closureTypeName() addSubst = false case 't': ret = st.unnamedTypeName() addSubst = false default: st.advance(1) n := st.sourceName() if len(st.str) > 0 && st.str[0] == 'I' { args := st.templateArgs() n = &Template{Name: n, Args: args} } t := st.demangleType(isCast) ret = &VendorQualifier{Qualifier: n, Type: t} } case 'D': st.advance(1) if len(st.str) == 0 { st.fail("expected D code for type") } addSubst = false c2 := st.str[0] st.advance(1) switch c2 { case 'T', 't': // decltype(expression) ret = st.expression() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after expression in type") } st.advance(1) ret = &Decltype{Expr: ret} addSubst = true case 'p': t := st.demangleType(isCast) pack := st.findArgumentPack(t) ret = &PackExpansion{Base: t, Pack: pack} addSubst = true case 'a': ret = &Name{Name: "auto"} case 'f': ret = &BuiltinType{Name: "decimal32"} case 'd': ret = &BuiltinType{Name: "decimal64"} case 'e': ret = &BuiltinType{Name: "decimal128"} case 'h': ret = &BuiltinType{Name: "half"} case 's': ret = &BuiltinType{Name: "char16_t"} case 'i': ret = &BuiltinType{Name: "char32_t"} case 'n': ret = &BuiltinType{Name: "decltype(nullptr)"} case 'F': accum := false if len(st.str) > 0 && isDigit(st.str[0]) { accum = true // We don't care about the bits. _ = st.number() } base := st.demangleType(isCast) if len(st.str) > 0 && isDigit(st.str[0]) { // We don't care about the bits. st.number() } sat := false if len(st.str) > 0 { if st.str[0] == 's' { sat = true } st.advance(1) } ret = &FixedType{Base: base, Accum: accum, Sat: sat} case 'v': ret = st.vectorType(isCast) addSubst = true default: st.fail("unrecognized D code in type") } default: st.fail("unrecognized type code") } if addSubst { if sub != nil { st.subs.add(sub) } else { st.subs.add(ret) } } if len(q) > 0 { if _, ok := ret.(*FunctionType); ok { ret = &MethodWithQualifiers{Method: ret, Qualifiers: q, RefQualifier: ""} } else if mwq, ok := ret.(*MethodWithQualifiers); ok { // Merge adjacent qualifiers. This case // happens with a function with a trailing // ref-qualifier. mwq.Qualifiers = mergeQualifiers(q, mwq.Qualifiers) } else { // Merge adjacent qualifiers. This case // happens with multi-dimensional array types. if qsub, ok := ret.(*TypeWithQualifiers); ok { q = mergeQualifiers(q, qsub.Qualifiers) ret = qsub.Base } ret = &TypeWithQualifiers{Base: ret, Qualifiers: q} } st.subs.add(ret) } return ret } // demangleCastTemplateArgs is for a rather hideous parse. When we // see a template-param followed by a template-args, we need to decide // whether we have a template-param or a template-template-param. // Normally it is template-template-param, meaning that we pick up the // template arguments here. But, if we are parsing the type for a // cast operator, then the only way this can be template-template-param // is if there is another set of template-args immediately after this // set. That would look like this: // // // -> // -> // -> // -> // -> // -> cv // -> cv // // Otherwise, we have this derivation: // // // -> // -> // -> // -> // -> // -> cv // -> cv // // in which the template-args are actually part of the prefix. For // the special case where this arises, demangleType is called with // isCast as true. This function is then responsible for checking // whether we see but there is not // another following . In that case, we reset the // parse and just return the . func (st *state) demangleCastTemplateArgs(tp AST, addSubst bool) AST { save := st.copy() var args []AST failed := false func() { defer func() { if r := recover(); r != nil { if _, ok := r.(demangleErr); ok { failed = true } else { panic(r) } } }() args = st.templateArgs() }() if !failed && len(st.str) > 0 && st.str[0] == 'I' { if addSubst { st.subs.add(tp) } return &Template{Name: tp, Args: args} } // Reset back to before we started reading the template arguments. // They will be read again by st.prefix. *st = *save return tp } // mergeQualifiers merges two qualifer lists into one. func mergeQualifiers(q1, q2 Qualifiers) Qualifiers { m := make(map[string]bool) for _, qual := range q1 { m[qual] = true } for _, qual := range q2 { if !m[qual] { q1 = append(q1, qual) m[qual] = true } } return q1 } // qualifiers maps from the character used in the mangled name to the // string to print. var qualifiers = map[byte]string{ 'r': "restrict", 'V': "volatile", 'K': "const", } // ::= [r] [V] [K] func (st *state) cvQualifiers() Qualifiers { var q Qualifiers for len(st.str) > 0 { if qv, ok := qualifiers[st.str[0]]; ok { q = append([]string{qv}, q...) st.advance(1) } else if len(st.str) > 1 && st.str[:2] == "Dx" { q = append([]string{"transaction_safe"}, q...) st.advance(2) } else { break } } return q } // ::= R // ::= O func (st *state) refQualifier() string { if len(st.str) > 0 { switch st.str[0] { case 'R': st.advance(1) return "&" case 'O': st.advance(1) return "&&" } } return "" } // + func (st *state) parmlist() []AST { var ret []AST for { if len(st.str) < 1 { break } if st.str[0] == 'E' || st.str[0] == '.' { break } if (st.str[0] == 'R' || st.str[0] == 'O') && len(st.str) > 1 && st.str[1] == 'E' { // This is a function ref-qualifier. break } ptype := st.demangleType(false) ret = append(ret, ptype) } // There should always be at least one type. A function that // takes no arguments will have a single parameter type // "void". if len(ret) == 0 { st.fail("expected at least one type in type list") } // Omit a single parameter type void. if len(ret) == 1 { if bt, ok := ret[0].(*BuiltinType); ok && bt.Name == "void" { ret = nil } } return ret } // ::= F [Y] [] E func (st *state) functionType() AST { st.checkChar('F') if len(st.str) > 0 && st.str[0] == 'Y' { // Function has C linkage. We don't print this. st.advance(1) } ret := st.bareFunctionType(true) r := st.refQualifier() if r != "" { ret = &MethodWithQualifiers{Method: ret, Qualifiers: nil, RefQualifier: r} } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after function type") } st.advance(1) return ret } // ::= [J]+ func (st *state) bareFunctionType(hasReturnType bool) AST { if len(st.str) > 0 && st.str[0] == 'J' { hasReturnType = true st.advance(1) } var returnType AST if hasReturnType { returnType = st.demangleType(false) } types := st.parmlist() return &FunctionType{Return: returnType, Args: types} } // ::= A <(positive dimension) number> _ <(element) type> // ::= A [<(dimension) expression>] _ <(element) type> func (st *state) arrayType(isCast bool) AST { st.checkChar('A') if len(st.str) == 0 { st.fail("missing array dimension") } var dim AST if st.str[0] == '_' { dim = &Name{Name: ""} } else if isDigit(st.str[0]) { i := 1 for len(st.str) > i && isDigit(st.str[i]) { i++ } dim = &Name{Name: st.str[:i]} st.advance(i) } else { dim = st.expression() } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after dimension") } st.advance(1) t := st.demangleType(isCast) arr := &ArrayType{Dimension: dim, Element: t} // Qualifiers on the element of an array type go on the whole // array type. if q, ok := arr.Element.(*TypeWithQualifiers); ok { return &TypeWithQualifiers{Base: &ArrayType{Dimension: dim, Element: q.Base}, Qualifiers: q.Qualifiers} } return arr } // ::= Dv _ // ::= Dv _ _ func (st *state) vectorType(isCast bool) AST { if len(st.str) == 0 { st.fail("expected vector dimension") } var dim AST if st.str[0] == '_' { st.advance(1) dim = st.expression() } else { num := st.number() dim = &Name{Name: fmt.Sprintf("%d", num)} } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after vector dimension") } st.advance(1) t := st.demangleType(isCast) return &VectorType{Dimension: dim, Base: t} } // ::= M <(class) type> <(member) type> func (st *state) pointerToMemberType(isCast bool) AST { st.checkChar('M') cl := st.demangleType(false) // The ABI says, "The type of a non-static member function is // considered to be different, for the purposes of // substitution, from the type of a namespace-scope or static // member function whose type appears similar. The types of // two non-static member functions are considered to be // different, for the purposes of substitution, if the // functions are members of different classes. In other words, // for the purposes of substitution, the class of which the // function is a member is considered part of the type of // function." // // For a pointer to member function, this call to demangleType // will end up adding a (possibly qualified) non-member // function type to the substitution table, which is not // correct; however, the member function type will never be // used in a substitution, so putting the wrong type in the // substitution table is harmless. mem := st.demangleType(isCast) return &PtrMem{Class: cl, Member: mem} } // _ */ func (st *state) compactNumber() int { if len(st.str) == 0 { st.fail("missing index") } if st.str[0] == '_' { st.advance(1) return 0 } else if st.str[0] == 'n' { st.fail("unexpected negative number") } n := st.number() if len(st.str) == 0 || st.str[0] != '_' { st.fail("missing underscore after number") } st.advance(1) return n + 1 } // ::= T_ // ::= T <(parameter-2 non-negative) number> _ // // When a template parameter is a substitution candidate, any // reference to that substitution refers to the template parameter // with the same index in the currently active template, not to // whatever the template parameter would be expanded to here. We sort // this out in substitution and simplify. func (st *state) templateParam() AST { if len(st.templates) == 0 { st.fail("template parameter not in scope of template") } off := st.off st.checkChar('T') n := st.compactNumber() template := st.templates[len(st.templates)-1] if template == nil { // We are parsing a cast operator. If the cast is // itself a template, then this is a forward // reference. Fill it in later. return &TemplateParam{Index: n, Template: nil} } if n >= len(template.Args) { st.failEarlier(fmt.Sprintf("template index out of range (%d >= %d)", n, len(template.Args)), st.off-off) } return &TemplateParam{Index: n, Template: template} } // setTemplate sets the Template field of any TemplateParam's in a. // This handles the forward referencing template parameters found in // cast operators. func (st *state) setTemplate(a AST, tmpl *Template) { var seen []AST a.Traverse(func(a AST) bool { switch a := a.(type) { case *TemplateParam: if a.Template != nil { if tmpl != nil { st.fail("duplicate template parameters") } return false } if tmpl == nil { st.fail("cast template parameter not in scope of template") } if a.Index >= len(tmpl.Args) { st.fail(fmt.Sprintf("cast template index out of range (%d >= %d)", a.Index, len(tmpl.Args))) } a.Template = tmpl return false default: for _, v := range seen { if v == a { return false } } seen = append(seen, a) return true } }) } // clearTemplateArgs gives an error for any unset Template field in // args. This handles erroneous cases where a cast operator with a // forward referenced template is in the scope of another cast // operator. func (st *state) clearTemplateArgs(args []AST) { for _, a := range args { st.setTemplate(a, nil) } } // ::= I + E func (st *state) templateArgs() []AST { if len(st.str) == 0 || (st.str[0] != 'I' && st.str[0] != 'J') { panic("internal error") } st.advance(1) var ret []AST for len(st.str) == 0 || st.str[0] != 'E' { arg := st.templateArg() ret = append(ret, arg) } st.advance(1) return ret } // ::= // ::= X E // ::= func (st *state) templateArg() AST { if len(st.str) == 0 { st.fail("missing template argument") } switch st.str[0] { case 'X': st.advance(1) expr := st.expression() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("missing end of expression") } st.advance(1) return expr case 'L': return st.exprPrimary() case 'I', 'J': args := st.templateArgs() return &ArgumentPack{Args: args} default: return st.demangleType(false) } } // exprList parses a sequence of expressions up to a terminating character. func (st *state) exprList(stop byte) AST { if len(st.str) > 0 && st.str[0] == stop { st.advance(1) return &ExprList{Exprs: nil} } var exprs []AST for { e := st.expression() exprs = append(exprs, e) if len(st.str) > 0 && st.str[0] == stop { st.advance(1) break } } return &ExprList{Exprs: exprs} } // ::= <(unary) operator-name> // ::= <(binary) operator-name> // ::= <(trinary) operator-name> // ::= cl + E // ::= st // ::= // ::= sr // ::= sr // ::= func (st *state) expression() AST { if len(st.str) == 0 { st.fail("expected expression") } if st.str[0] == 'L' { return st.exprPrimary() } else if st.str[0] == 'T' { return st.templateParam() } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' { st.advance(2) if len(st.str) == 0 { st.fail("expected unresolved type") } switch st.str[0] { case 'T', 'D', 'S': t := st.demangleType(false) n := st.baseUnresolvedName() n = &Qualified{Scope: t, Name: n, LocalName: false} if len(st.str) > 0 && st.str[0] == 'I' { args := st.templateArgs() n = &Template{Name: n, Args: args} } return n default: var s AST if st.str[0] == 'N' { st.advance(1) s = st.demangleType(false) } for len(st.str) == 0 || st.str[0] != 'E' { // GCC does not seem to follow the ABI here. // It can emit type/name without an 'E'. if s != nil && len(st.str) > 0 && !isDigit(st.str[0]) { if q, ok := s.(*Qualified); ok { a := q.Scope if t, ok := a.(*Template); ok { st.subs.add(t.Name) st.subs.add(t) } else { st.subs.add(a) } return s } } n := st.sourceName() if len(st.str) > 0 && st.str[0] == 'I' { st.subs.add(n) args := st.templateArgs() n = &Template{Name: n, Args: args} } if s == nil { s = n } else { s = &Qualified{Scope: s, Name: n, LocalName: false} } st.subs.add(s) } if s == nil { st.fail("missing scope in unresolved name") } st.advance(1) n := st.baseUnresolvedName() return &Qualified{Scope: s, Name: n, LocalName: false} } } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'p' { st.advance(2) e := st.expression() pack := st.findArgumentPack(e) return &PackExpansion{Base: e, Pack: pack} } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'Z' { st.advance(2) off := st.off e := st.expression() ap := st.findArgumentPack(e) if ap == nil { st.failEarlier("missing argument pack", st.off-off) } return &SizeofPack{Pack: ap} } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'P' { st.advance(2) var args []AST for len(st.str) == 0 || st.str[0] != 'E' { arg := st.templateArg() args = append(args, arg) } st.advance(1) return &SizeofArgs{Args: args} } else if st.str[0] == 'f' && len(st.str) > 1 && st.str[1] == 'p' { st.advance(2) if len(st.str) > 0 && st.str[0] == 'T' { st.advance(1) return &FunctionParam{Index: 0} } else { index := st.compactNumber() return &FunctionParam{Index: index + 1} } } else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') { if st.str[0] == 'o' { // Skip operator function ID. st.advance(2) } n, _ := st.unqualifiedName() if len(st.str) > 0 && st.str[0] == 'I' { args := st.templateArgs() n = &Template{Name: n, Args: args} } return n } else if (st.str[0] == 'i' || st.str[0] == 't') && len(st.str) > 1 && st.str[1] == 'l' { // Brace-enclosed initializer list. c := st.str[0] st.advance(2) var t AST if c == 't' { t = st.demangleType(false) } exprs := st.exprList('E') return &InitializerList{Type: t, Exprs: exprs} } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 't' { o, _ := st.operatorName(true) t := st.demangleType(false) return &Unary{Op: o, Expr: t, Suffix: false, SizeofType: true} } else { if len(st.str) < 2 { st.fail("missing operator code") } code := st.str[:2] o, args := st.operatorName(true) switch args { case 0: return &Nullary{Op: o} case 1: suffix := false if code == "pp" || code == "mm" { if len(st.str) > 0 && st.str[0] == '_' { st.advance(1) } else { suffix = true } } var operand AST if _, ok := o.(*Cast); ok && len(st.str) > 0 && st.str[0] == '_' { st.advance(1) operand = st.exprList('E') } else { operand = st.expression() } return &Unary{Op: o, Expr: operand, Suffix: suffix, SizeofType: false} case 2: var left, right AST if code == "sc" || code == "dc" || code == "cc" || code == "rc" { left = st.demangleType(false) } else if code[0] == 'f' { left, _ = st.operatorName(true) right = st.expression() return &Fold{Left: code[1] == 'l', Op: left, Arg1: right, Arg2: nil} } else { left = st.expression() } if code == "cl" { right = st.exprList('E') } else if code == "dt" || code == "pt" { right, _ = st.unqualifiedName() if len(st.str) > 0 && st.str[0] == 'I' { args := st.templateArgs() right = &Template{Name: right, Args: args} } } else { right = st.expression() } return &Binary{Op: o, Left: left, Right: right} case 3: if code[0] == 'n' { if code[1] != 'w' && code[1] != 'a' { panic("internal error") } place := st.exprList('_') if place.(*ExprList).Exprs == nil { place = nil } t := st.demangleType(false) var ini AST if len(st.str) > 0 && st.str[0] == 'E' { st.advance(1) } else if len(st.str) > 1 && st.str[0] == 'p' && st.str[1] == 'i' { // Parenthesized initializer. st.advance(2) ini = st.exprList('E') } else if len(st.str) > 1 && st.str[0] == 'i' && st.str[1] == 'l' { // Initializer list. ini = st.expression() } else { st.fail("unrecognized new initializer") } return &New{Op: o, Place: place, Type: t, Init: ini} } else if code[0] == 'f' { first, _ := st.operatorName(true) second := st.expression() third := st.expression() return &Fold{Left: code[1] == 'L', Op: first, Arg1: second, Arg2: third} } else { first := st.expression() second := st.expression() third := st.expression() return &Trinary{Op: o, First: first, Second: second, Third: third} } default: st.fail(fmt.Sprintf("unsupported number of operator arguments: %d", args)) panic("not reached") } } } // ::= // ::= on // ::= on // ::= dn // // ::= [ ] func (st *state) baseUnresolvedName() AST { var n AST if len(st.str) >= 2 && st.str[:2] == "on" { st.advance(2) n, _ = st.operatorName(true) } else if len(st.str) >= 2 && st.str[:2] == "dn" { st.advance(2) if len(st.str) > 0 && isDigit(st.str[0]) { n = st.sourceName() } else { n = st.demangleType(false) } n = &Destructor{Name: n} } else if len(st.str) > 0 && isDigit(st.str[0]) { n = st.sourceName() } else { // GCC seems to not follow the ABI here: it can have // an operator name without on. // See https://gcc.gnu.org/PR70182. n, _ = st.operatorName(true) } if len(st.str) > 0 && st.str[0] == 'I' { args := st.templateArgs() n = &Template{Name: n, Args: args} } return n } // ::= L <(value) number> E // ::= L <(value) float> E // ::= L E func (st *state) exprPrimary() AST { st.checkChar('L') if len(st.str) == 0 { st.fail("expected primary expression") } // Check for 'Z' here because g++ incorrectly omitted the // underscore until -fabi-version=3. var ret AST if st.str[0] == '_' || st.str[0] == 'Z' { if st.str[0] == '_' { st.advance(1) } if len(st.str) == 0 || st.str[0] != 'Z' { st.fail("expected mangled name") } st.advance(1) ret = st.encoding(true, notForLocalName) } else { t := st.demangleType(false) neg := false if len(st.str) > 0 && st.str[0] == 'n' { neg = true st.advance(1) } if len(st.str) > 0 && st.str[0] == 'E' { st.fail("missing literal value") } i := 0 for len(st.str) > i && st.str[i] != 'E' { i++ } val := st.str[:i] st.advance(i) ret = &Literal{Type: t, Val: val, Neg: neg} } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after literal") } st.advance(1) return ret } // ::= _ <(non-negative) number> func (st *state) discriminator(a AST) AST { if len(st.str) == 0 || st.str[0] != '_' { return a } off := st.off st.advance(1) d := st.number() if d < 0 { st.failEarlier("invalid negative discriminator", st.off-off) } // We don't currently print out the discriminator, so we don't // save it. return a } // ::= Ul E [ ] _ func (st *state) closureTypeName() AST { st.checkChar('U') st.checkChar('l') types := st.parmlist() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after closure type name") } st.advance(1) num := st.compactNumber() ret := &Closure{Types: types, Num: num} st.subs.add(ret) return ret } // ::= Ut [ ] _ func (st *state) unnamedTypeName() AST { st.checkChar('U') st.checkChar('t') num := st.compactNumber() ret := &UnnamedType{Num: num} st.subs.add(ret) return ret } // Recognize a clone suffix. These are not part of the mangling API, // but are added by GCC when cloning functions. func (st *state) cloneSuffix(a AST) AST { i := 0 if len(st.str) > 1 && st.str[0] == '.' && (isLower(st.str[1]) || st.str[1] == '_') { i += 2 for len(st.str) > i && (isLower(st.str[i]) || st.str[i] == '_') { i++ } } for len(st.str) > i+1 && st.str[i] == '.' && isDigit(st.str[i+1]) { i += 2 for len(st.str) > i && isDigit(st.str[i]) { i++ } } suffix := st.str[:i] st.advance(i) return &Clone{Base: a, Suffix: suffix} } // substitutions is the list of substitution candidates that may // appear later in the string. type substitutions []AST // add adds a new substitution candidate. func (subs *substitutions) add(a AST) { *subs = append(*subs, a) } // subAST maps standard substitution codes to the corresponding AST. var subAST = map[byte]AST{ 't': &Name{Name: "std"}, 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}}, 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}}, 's': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "string"}}, 'i': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "istream"}}, 'o': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "ostream"}}, 'd': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "iostream"}}, } // verboseAST maps standard substitution codes to the long form of the // corresponding AST. We use this when the Verbose option is used, to // match the standard demangler. var verboseAST = map[byte]AST{ 't': &Name{Name: "std"}, 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}}, 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}}, // std::basic_string, std::allocator > 's': &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}}, Args: []AST{ &BuiltinType{Name: "char"}, &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}}, Args: []AST{&BuiltinType{Name: "char"}}}, &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}}, Args: []AST{&BuiltinType{Name: "char"}}}}}, // std::basic_istream > 'i': &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_istream"}}, Args: []AST{ &BuiltinType{Name: "char"}, &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}}, Args: []AST{&BuiltinType{Name: "char"}}}}}, // std::basic_ostream > 'o': &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_ostream"}}, Args: []AST{ &BuiltinType{Name: "char"}, &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}}, Args: []AST{&BuiltinType{Name: "char"}}}}}, // std::basic_iostream > 'd': &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_iostream"}}, Args: []AST{ &BuiltinType{Name: "char"}, &Template{ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}}, Args: []AST{&BuiltinType{Name: "char"}}}}}, } // ::= S _ // ::= S_ // ::= St // ::= Sa // ::= Sb // ::= Ss // ::= Si // ::= So // ::= Sd func (st *state) substitution(forPrefix bool) AST { st.checkChar('S') if len(st.str) == 0 { st.fail("missing substitution index") } c := st.str[0] st.advance(1) dec := 1 if c == '_' || isDigit(c) || isUpper(c) { id := 0 if c != '_' { for c != '_' { // Don't overflow a 32-bit int. if id >= 0x80000000/36-36 { st.fail("substitution index overflow") } if isDigit(c) { id = id*36 + int(c-'0') } else if isUpper(c) { id = id*36 + int(c-'A') + 10 } else { st.fail("invalid character in substitution index") } if len(st.str) == 0 { st.fail("missing end to substitution index") } c = st.str[0] st.advance(1) dec++ } id++ } if id >= len(st.subs) { st.failEarlier(fmt.Sprintf("substitution index out of range (%d >= %d)", id, len(st.subs)), dec) } ret := st.subs[id] // We need to update any references to template // parameters to refer to the currently active // template. copy := func(a AST) AST { tp, ok := a.(*TemplateParam) if !ok { return nil } if len(st.templates) == 0 { st.failEarlier("substituted template parameter not in scope of template", dec) } template := st.templates[len(st.templates)-1] if template == nil { // This template parameter is within // the scope of a cast operator. return &TemplateParam{Index: tp.Index, Template: nil} } if tp.Index >= len(template.Args) { st.failEarlier(fmt.Sprintf("substituted template index out of range (%d >= %d)", tp.Index, len(template.Args)), dec) } return &TemplateParam{Index: tp.Index, Template: template} } var seen []AST skip := func(a AST) bool { if _, ok := a.(*Typed); ok { return true } for _, v := range seen { if v == a { return true } } seen = append(seen, a) return false } if c := ret.Copy(copy, skip); c != nil { return c } return ret } else { m := subAST if st.verbose { m = verboseAST } // For compatibility with the standard demangler, use // a longer name for a constructor or destructor. if forPrefix && len(st.str) > 0 && (st.str[0] == 'C' || st.str[0] == 'D') { m = verboseAST } a, ok := m[c] if !ok { st.failEarlier("unrecognized substitution code", 1) } if len(st.str) > 0 && st.str[0] == 'B' { a = st.taggedName(a) } return a } } // isDigit returns whetner c is a digit for demangling purposes. func isDigit(c byte) bool { return c >= '0' && c <= '9' } // isUpper returns whether c is an upper case letter for demangling purposes. func isUpper(c byte) bool { return c >= 'A' && c <= 'Z' } // isLower returns whether c is a lower case letter for demangling purposes. func isLower(c byte) bool { return c >= 'a' && c <= 'z' } // simplify replaces template parameters with their expansions, and // merges qualifiers. func simplify(a AST) AST { var seen []AST skip := func(a AST) bool { for _, v := range seen { if v == a { return true } } seen = append(seen, a) return false } if r := a.Copy(simplifyOne, skip); r != nil { return r } return a } // simplifyOne simplifies a single AST. It returns nil if there is // nothing to do. func simplifyOne(a AST) AST { switch a := a.(type) { case *TemplateParam: if a.Template != nil && a.Index < len(a.Template.Args) { return a.Template.Args[a.Index] } case *MethodWithQualifiers: if m, ok := a.Method.(*MethodWithQualifiers); ok { ref := a.RefQualifier if ref == "" { ref = m.RefQualifier } else if m.RefQualifier != "" { if ref == "&" || m.RefQualifier == "&" { ref = "&" } } return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: ref} } if t, ok := a.Method.(*TypeWithQualifiers); ok { return &MethodWithQualifiers{Method: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers), RefQualifier: a.RefQualifier} } case *TypeWithQualifiers: if ft, ok := a.Base.(*FunctionType); ok { return &MethodWithQualifiers{Method: ft, Qualifiers: a.Qualifiers, RefQualifier: ""} } if t, ok := a.Base.(*TypeWithQualifiers); ok { return &TypeWithQualifiers{Base: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers)} } if m, ok := a.Base.(*MethodWithQualifiers); ok { return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: m.RefQualifier} } case *ReferenceType: if rt, ok := a.Base.(*ReferenceType); ok { return rt } if rrt, ok := a.Base.(*RvalueReferenceType); ok { return &ReferenceType{Base: rrt.Base} } case *RvalueReferenceType: if rrt, ok := a.Base.(*RvalueReferenceType); ok { return rrt } if rt, ok := a.Base.(*ReferenceType); ok { return rt } case *ArrayType: // Qualifiers on the element of an array type // go on the whole array type. if q, ok := a.Element.(*TypeWithQualifiers); ok { return &TypeWithQualifiers{ Base: &ArrayType{Dimension: a.Dimension, Element: q.Base}, Qualifiers: q.Qualifiers, } } case *PackExpansion: // Expand the pack and replace it with a list of // expressions. if a.Pack != nil { exprs := make([]AST, len(a.Pack.Args)) for i, arg := range a.Pack.Args { copy := func(sub AST) AST { // Replace the ArgumentPack // with a specific argument. if sub == a.Pack { return arg } // Copy everything else. return nil } var seen []AST skip := func(sub AST) bool { // Don't traverse into another // pack expansion. if _, ok := sub.(*PackExpansion); ok { return true } for _, v := range seen { if v == sub { return true } } seen = append(seen, sub) return false } b := a.Base.Copy(copy, skip) if b == nil { b = a.Base } exprs[i] = simplify(b) } return &ExprList{Exprs: exprs} } } return nil } // findArgumentPack walks the AST looking for the argument pack for a // pack expansion. We find it via a template parameter. func (st *state) findArgumentPack(a AST) *ArgumentPack { var seen []AST var ret *ArgumentPack a.Traverse(func(a AST) bool { if ret != nil { return false } switch a := a.(type) { case *TemplateParam: if a.Template == nil || a.Index >= len(a.Template.Args) { return true } if pack, ok := a.Template.Args[a.Index].(*ArgumentPack); ok { ret = pack return false } case *PackExpansion, *Closure, *Name: return false case *TaggedName, *Operator, *BuiltinType, *FunctionParam: return false case *UnnamedType, *FixedType, *DefaultArg: return false } for _, v := range seen { if v == a { return false } } seen = append(seen, a) return true }) return ret }