// 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 import ( "fmt" "strings" ) // AST is an abstract syntax tree representing a C++ declaration. // This is sufficient for the demangler but is by no means a general C++ AST. // This abstract syntax tree is only used for C++ symbols, not Rust symbols. type AST interface { // Internal method to convert to demangled string. print(*printState) // Traverse each element of an AST. If the function returns // false, traversal of children of that element is skipped. Traverse(func(AST) bool) // Copy an AST with possible transformations. // If the skip function returns true, no copy is required. // If the copy function returns nil, no copy is required. // The Copy method will do the right thing if copy returns nil // for some components of an AST but not others, so a good // copy function will only return non-nil for AST values that // need to change. // Copy itself returns either a copy or nil. Copy(copy func(AST) AST, skip func(AST) bool) AST // Implement the fmt.GoStringer interface. GoString() string goString(indent int, field string) string } // ASTToString returns the demangled name of the AST. func ASTToString(a AST, options ...Option) string { tparams := true enclosingParams := true llvmStyle := false max := 0 for _, o := range options { switch { case o == NoTemplateParams: tparams = false case o == NoEnclosingParams: enclosingParams = false case o == LLVMStyle: llvmStyle = true case isMaxLength(o): max = maxLength(o) } } ps := printState{ tparams: tparams, enclosingParams: enclosingParams, llvmStyle: llvmStyle, max: max, scopes: 1, } a.print(&ps) s := ps.buf.String() if max > 0 && len(s) > max { s = s[:max] } return s } // The printState type holds information needed to print an AST. type printState struct { tparams bool // whether to print template parameters enclosingParams bool // whether to print enclosing parameters llvmStyle bool max int // maximum output length // The scopes field is used to avoid unnecessary parentheses // around expressions that use > (or >>). It is incremented if // we output a parenthesis or something else that means that > // or >> won't be treated as ending a template. It starts out // as 1, and is set to 0 when we start writing template // arguments. We add parentheses around expressions using > if // scopes is 0. The effect is that an expression with > gets // parentheses if used as a template argument that is not // inside some other set of parentheses. scopes int buf strings.Builder last byte // Last byte written to buffer. // The inner field is a list of items to print for a type // name. This is used by types to implement the inside-out // C++ declaration syntax. inner []AST // The printing field is a list of items we are currently // printing. This avoids endless recursion if a substitution // reference creates a cycle in the graph. printing []AST } // writeByte adds a byte to the string being printed. func (ps *printState) writeByte(b byte) { ps.last = b ps.buf.WriteByte(b) } // writeString adds a string to the string being printed. func (ps *printState) writeString(s string) { if len(s) > 0 { ps.last = s[len(s)-1] } ps.buf.WriteString(s) } // Print an AST. func (ps *printState) print(a AST) { if ps.max > 0 && ps.buf.Len() > ps.max { return } c := 0 for _, v := range ps.printing { if v == a { // We permit the type to appear once, and // return without printing anything if we see // it twice. This is for a case like // _Z6outer2IsEPFilES1_, where the // substitution is printed differently the // second time because the set of inner types // is different. c++ if c > 1 { return } } } ps.printing = append(ps.printing, a) a.print(ps) ps.printing = ps.printing[:len(ps.printing)-1] } // printList prints a list of AST values separated by commas, // optionally skipping some. func (ps *printState) printList(args []AST, skip func(AST) bool) { first := true for _, a := range args { if skip != nil && skip(a) { continue } if !first { ps.writeString(", ") } needsParen := false if ps.llvmStyle { if p, ok := a.(hasPrec); ok { if p.prec() >= precComma { needsParen = true } } } if needsParen { ps.startScope('(') } ps.print(a) if needsParen { ps.endScope(')') } first = false } } // startScope starts a scope. This is used to decide whether we need // to parenthesize an expression using > or >>. func (ps *printState) startScope(b byte) { ps.scopes++ ps.writeByte(b) } // endScope closes a scope. func (ps *printState) endScope(b byte) { ps.scopes-- ps.writeByte(b) } // precedence is used for operator precedence. This is used to avoid // unnecessary parentheses when printing expressions in the LLVM style. type precedence int // The precedence values, in order from high to low. const ( precPrimary precedence = iota precPostfix precUnary precCast precPtrMem precMul precAdd precShift precSpaceship precRel precEqual precAnd precXor precOr precLogicalAnd precLogicalOr precCond precAssign precComma precDefault ) // hasPrec matches the AST nodes that have a prec method that returns // the node's precedence. type hasPrec interface { prec() precedence } // Name is an unqualified name. type Name struct { Name string } func (n *Name) print(ps *printState) { ps.writeString(n.Name) } func (n *Name) Traverse(fn func(AST) bool) { fn(n) } func (n *Name) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(n) { return nil } return fn(n) } func (n *Name) GoString() string { return n.goString(0, "Name: ") } func (n *Name) goString(indent int, field string) string { return fmt.Sprintf("%*s%s%s", indent, "", field, n.Name) } func (n *Name) prec() precedence { return precPrimary } // Typed is a typed name. type Typed struct { Name AST Type AST } func (t *Typed) print(ps *printState) { // We are printing a typed name, so ignore the current set of // inner names to print. Pass down our name as the one to use. holdInner := ps.inner defer func() { ps.inner = holdInner }() ps.inner = []AST{t} ps.print(t.Type) if len(ps.inner) > 0 { // The type did not print the name; print it now in // the default location. ps.writeByte(' ') ps.print(t.Name) } } func (t *Typed) printInner(ps *printState) { ps.print(t.Name) } func (t *Typed) Traverse(fn func(AST) bool) { if fn(t) { t.Name.Traverse(fn) t.Type.Traverse(fn) } } func (t *Typed) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(t) { return nil } name := t.Name.Copy(fn, skip) typ := t.Type.Copy(fn, skip) if name == nil && typ == nil { return fn(t) } if name == nil { name = t.Name } if typ == nil { typ = t.Type } t = &Typed{Name: name, Type: typ} if r := fn(t); r != nil { return r } return t } func (t *Typed) GoString() string { return t.goString(0, "") } func (t *Typed) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTyped:\n%s\n%s", indent, "", field, t.Name.goString(indent+2, "Name: "), t.Type.goString(indent+2, "Type: ")) } // Qualified is a name in a scope. type Qualified struct { Scope AST Name AST // The LocalName field is true if this is parsed as a // <local-name>. We shouldn't really need this, but in some // cases (for the unary sizeof operator) the standard // demangler prints a local name slightly differently. We // keep track of this for compatibility. LocalName bool // A full local name encoding } func (q *Qualified) print(ps *printState) { ps.print(q.Scope) ps.writeString("::") ps.print(q.Name) } func (q *Qualified) Traverse(fn func(AST) bool) { if fn(q) { q.Scope.Traverse(fn) q.Name.Traverse(fn) } } func (q *Qualified) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(q) { return nil } scope := q.Scope.Copy(fn, skip) name := q.Name.Copy(fn, skip) if scope == nil && name == nil { return fn(q) } if scope == nil { scope = q.Scope } if name == nil { name = q.Name } q = &Qualified{Scope: scope, Name: name, LocalName: q.LocalName} if r := fn(q); r != nil { return r } return q } func (q *Qualified) GoString() string { return q.goString(0, "") } func (q *Qualified) goString(indent int, field string) string { s := "" if q.LocalName { s = " LocalName: true" } return fmt.Sprintf("%*s%sQualified:%s\n%s\n%s", indent, "", field, s, q.Scope.goString(indent+2, "Scope: "), q.Name.goString(indent+2, "Name: ")) } func (q *Qualified) prec() precedence { return precPrimary } // Template is a template with arguments. type Template struct { Name AST Args []AST } func (t *Template) print(ps *printState) { // Inner types apply to the template as a whole, they don't // cross over into the template. holdInner := ps.inner defer func() { ps.inner = holdInner }() ps.inner = nil ps.print(t.Name) if !ps.tparams { // Do not print template parameters. return } // We need an extra space after operator<. if ps.last == '<' { ps.writeByte(' ') } scopes := ps.scopes ps.scopes = 0 ps.writeByte('<') ps.printList(t.Args, ps.isEmpty) if ps.last == '>' && !ps.llvmStyle { // Avoid syntactic ambiguity in old versions of C++. ps.writeByte(' ') } ps.writeByte('>') ps.scopes = scopes } func (t *Template) Traverse(fn func(AST) bool) { if fn(t) { t.Name.Traverse(fn) for _, a := range t.Args { a.Traverse(fn) } } } func (t *Template) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(t) { return nil } name := t.Name.Copy(fn, skip) changed := name != nil args := make([]AST, len(t.Args)) for i, a := range t.Args { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac changed = true } } if !changed { return fn(t) } if name == nil { name = t.Name } t = &Template{Name: name, Args: args} if r := fn(t); r != nil { return r } return t } func (t *Template) GoString() string { return t.goString(0, "") } func (t *Template) goString(indent int, field string) string { var args string if len(t.Args) == 0 { args = fmt.Sprintf("%*sArgs: nil", indent+2, "") } else { args = fmt.Sprintf("%*sArgs:", indent+2, "") for i, a := range t.Args { args += "\n" args += a.goString(indent+4, fmt.Sprintf("%d: ", i)) } } return fmt.Sprintf("%*s%sTemplate (%p):\n%s\n%s", indent, "", field, t, t.Name.goString(indent+2, "Name: "), args) } // TemplateParam is a template parameter. The Template field is // filled in while parsing the demangled string. We don't normally // see these while printing--they are replaced by the simplify // function. type TemplateParam struct { Index int Template *Template } func (tp *TemplateParam) print(ps *printState) { if tp.Template == nil { panic("TemplateParam Template field is nil") } if tp.Index >= len(tp.Template.Args) { panic("TemplateParam Index out of bounds") } ps.print(tp.Template.Args[tp.Index]) } func (tp *TemplateParam) Traverse(fn func(AST) bool) { fn(tp) // Don't traverse Template--it points elsewhere in the AST. } func (tp *TemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tp) { return nil } return fn(tp) } func (tp *TemplateParam) GoString() string { return tp.goString(0, "") } func (tp *TemplateParam) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTemplateParam: Template: %p; Index %d", indent, "", field, tp.Template, tp.Index) } // LambdaAuto is a lambda auto parameter. type LambdaAuto struct { Index int } func (la *LambdaAuto) print(ps *printState) { // We print the index plus 1 because that is what the standard // demangler does. if ps.llvmStyle { ps.writeString("auto") } else { fmt.Fprintf(&ps.buf, "auto:%d", la.Index+1) } } func (la *LambdaAuto) Traverse(fn func(AST) bool) { fn(la) } func (la *LambdaAuto) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(la) { return nil } return fn(la) } func (la *LambdaAuto) GoString() string { return la.goString(0, "") } func (la *LambdaAuto) goString(indent int, field string) string { return fmt.Sprintf("%*s%sLambdaAuto: Index %d", indent, "", field, la.Index) } // TemplateParamQualifiedArg is used when the mangled name includes // both the template parameter declaration and the template argument. // See https://github.com/itanium-cxx-abi/cxx-abi/issues/47. type TemplateParamQualifiedArg struct { Param AST Arg AST } func (tpqa *TemplateParamQualifiedArg) print(ps *printState) { // We only demangle the actual template argument. // That is what the LLVM demangler does. // The parameter disambiguates the argument, // but is hopefully not required by a human reader. ps.print(tpqa.Arg) } func (tpqa *TemplateParamQualifiedArg) Traverse(fn func(AST) bool) { if fn(tpqa) { tpqa.Param.Traverse(fn) tpqa.Arg.Traverse(fn) } } func (tpqa *TemplateParamQualifiedArg) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tpqa) { return nil } param := tpqa.Param.Copy(fn, skip) arg := tpqa.Arg.Copy(fn, skip) if param == nil && arg == nil { return fn(tpqa) } if param == nil { param = tpqa.Param } if arg == nil { arg = tpqa.Arg } tpqa = &TemplateParamQualifiedArg{Param: param, Arg: arg} if r := fn(tpqa); r != nil { return r } return tpqa } func (tpqa *TemplateParamQualifiedArg) GoString() string { return tpqa.goString(0, "") } func (tpqa *TemplateParamQualifiedArg) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTemplateParamQualifiedArg:\n%s\n%s", indent, "", field, tpqa.Param.goString(indent+2, "Param: "), tpqa.Arg.goString(indent+2, "Arg: ")) } // Qualifiers is an ordered list of type qualifiers. type Qualifiers struct { Qualifiers []AST } func (qs *Qualifiers) print(ps *printState) { first := true for _, q := range qs.Qualifiers { if !first { ps.writeByte(' ') } q.print(ps) first = false } } func (qs *Qualifiers) Traverse(fn func(AST) bool) { if fn(qs) { for _, q := range qs.Qualifiers { q.Traverse(fn) } } } func (qs *Qualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(qs) { return nil } changed := false qualifiers := make([]AST, len(qs.Qualifiers)) for i, q := range qs.Qualifiers { qc := q.Copy(fn, skip) if qc == nil { qualifiers[i] = q } else { qualifiers[i] = qc changed = true } } if !changed { return fn(qs) } qs = &Qualifiers{Qualifiers: qualifiers} if r := fn(qs); r != nil { return r } return qs } func (qs *Qualifiers) GoString() string { return qs.goString(0, "") } func (qs *Qualifiers) goString(indent int, field string) string { quals := fmt.Sprintf("%*s%s", indent, "", field) for _, q := range qs.Qualifiers { quals += "\n" quals += q.goString(indent+2, "") } return quals } // Qualifier is a single type qualifier. type Qualifier struct { Name string // qualifier name: const, volatile, etc. Exprs []AST // can be non-nil for noexcept and throw } func (q *Qualifier) print(ps *printState) { ps.writeString(q.Name) if len(q.Exprs) > 0 { ps.startScope('(') first := true for _, e := range q.Exprs { if el, ok := e.(*ExprList); ok && len(el.Exprs) == 0 { continue } if !first { ps.writeString(", ") } ps.print(e) first = false } ps.endScope(')') } } func (q *Qualifier) Traverse(fn func(AST) bool) { if fn(q) { for _, e := range q.Exprs { e.Traverse(fn) } } } func (q *Qualifier) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(q) { return nil } exprs := make([]AST, len(q.Exprs)) changed := false for i, e := range q.Exprs { ec := e.Copy(fn, skip) if ec == nil { exprs[i] = e } else { exprs[i] = ec changed = true } } if !changed { return fn(q) } q = &Qualifier{Name: q.Name, Exprs: exprs} if r := fn(q); r != nil { return r } return q } func (q *Qualifier) GoString() string { return q.goString(0, "Qualifier: ") } func (q *Qualifier) goString(indent int, field string) string { qs := fmt.Sprintf("%*s%s%s", indent, "", field, q.Name) if len(q.Exprs) > 0 { for i, e := range q.Exprs { qs += "\n" qs += e.goString(indent+2, fmt.Sprintf("%d: ", i)) } } return qs } // TypeWithQualifiers is a type with standard qualifiers. type TypeWithQualifiers struct { Base AST Qualifiers AST } func (twq *TypeWithQualifiers) print(ps *printState) { // Give the base type a chance to print the inner types. ps.inner = append(ps.inner, twq) ps.print(twq.Base) if len(ps.inner) > 0 { // The qualifier wasn't printed by Base. ps.writeByte(' ') ps.print(twq.Qualifiers) ps.inner = ps.inner[:len(ps.inner)-1] } } // Print qualifiers as an inner type by just printing the qualifiers. func (twq *TypeWithQualifiers) printInner(ps *printState) { ps.writeByte(' ') ps.print(twq.Qualifiers) } func (twq *TypeWithQualifiers) Traverse(fn func(AST) bool) { if fn(twq) { twq.Base.Traverse(fn) } } func (twq *TypeWithQualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(twq) { return nil } base := twq.Base.Copy(fn, skip) quals := twq.Qualifiers.Copy(fn, skip) if base == nil && quals == nil { return fn(twq) } if base == nil { base = twq.Base } if quals == nil { quals = twq.Qualifiers } twq = &TypeWithQualifiers{Base: base, Qualifiers: quals} if r := fn(twq); r != nil { return r } return twq } func (twq *TypeWithQualifiers) GoString() string { return twq.goString(0, "") } func (twq *TypeWithQualifiers) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTypeWithQualifiers:\n%s\n%s", indent, "", field, twq.Qualifiers.goString(indent+2, "Qualifiers: "), twq.Base.goString(indent+2, "Base: ")) } // MethodWithQualifiers is a method with qualifiers. type MethodWithQualifiers struct { Method AST Qualifiers AST RefQualifier string // "" or "&" or "&&" } func (mwq *MethodWithQualifiers) print(ps *printState) { // Give the base type a chance to print the inner types. ps.inner = append(ps.inner, mwq) ps.print(mwq.Method) if len(ps.inner) > 0 { if mwq.Qualifiers != nil { ps.writeByte(' ') ps.print(mwq.Qualifiers) } if mwq.RefQualifier != "" { ps.writeByte(' ') ps.writeString(mwq.RefQualifier) } ps.inner = ps.inner[:len(ps.inner)-1] } } func (mwq *MethodWithQualifiers) printInner(ps *printState) { if mwq.Qualifiers != nil { ps.writeByte(' ') ps.print(mwq.Qualifiers) } if mwq.RefQualifier != "" { ps.writeByte(' ') ps.writeString(mwq.RefQualifier) } } func (mwq *MethodWithQualifiers) Traverse(fn func(AST) bool) { if fn(mwq) { mwq.Method.Traverse(fn) } } func (mwq *MethodWithQualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(mwq) { return nil } method := mwq.Method.Copy(fn, skip) var quals AST if mwq.Qualifiers != nil { quals = mwq.Qualifiers.Copy(fn, skip) } if method == nil && quals == nil { return fn(mwq) } if method == nil { method = mwq.Method } if quals == nil { quals = mwq.Qualifiers } mwq = &MethodWithQualifiers{Method: method, Qualifiers: quals, RefQualifier: mwq.RefQualifier} if r := fn(mwq); r != nil { return r } return mwq } func (mwq *MethodWithQualifiers) GoString() string { return mwq.goString(0, "") } func (mwq *MethodWithQualifiers) goString(indent int, field string) string { var q string if mwq.Qualifiers != nil { q += "\n" + mwq.Qualifiers.goString(indent+2, "Qualifiers: ") } if mwq.RefQualifier != "" { if q != "" { q += "\n" } q += fmt.Sprintf("%*s%s%s", indent+2, "", "RefQualifier: ", mwq.RefQualifier) } return fmt.Sprintf("%*s%sMethodWithQualifiers:%s\n%s", indent, "", field, q, mwq.Method.goString(indent+2, "Method: ")) } // BuiltinType is a builtin type, like "int". type BuiltinType struct { Name string } func (bt *BuiltinType) print(ps *printState) { name := bt.Name if ps.llvmStyle && name == "decltype(nullptr)" { name = "std::nullptr_t" } ps.writeString(name) } func (bt *BuiltinType) Traverse(fn func(AST) bool) { fn(bt) } func (bt *BuiltinType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(bt) { return nil } return fn(bt) } func (bt *BuiltinType) GoString() string { return bt.goString(0, "") } func (bt *BuiltinType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sBuiltinType: %s", indent, "", field, bt.Name) } func (bt *BuiltinType) prec() precedence { return precPrimary } // printBase is common print code for types that are printed with a // simple suffix. func printBase(ps *printState, qual, base AST) { ps.inner = append(ps.inner, qual) ps.print(base) if len(ps.inner) > 0 { qual.(innerPrinter).printInner(ps) ps.inner = ps.inner[:len(ps.inner)-1] } } // PointerType is a pointer type. type PointerType struct { Base AST } func (pt *PointerType) print(ps *printState) { printBase(ps, pt, pt.Base) } func (pt *PointerType) printInner(ps *printState) { ps.writeString("*") } func (pt *PointerType) Traverse(fn func(AST) bool) { if fn(pt) { pt.Base.Traverse(fn) } } func (pt *PointerType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(pt) { return nil } base := pt.Base.Copy(fn, skip) if base == nil { return fn(pt) } pt = &PointerType{Base: base} if r := fn(pt); r != nil { return r } return pt } func (pt *PointerType) GoString() string { return pt.goString(0, "") } func (pt *PointerType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sPointerType:\n%s", indent, "", field, pt.Base.goString(indent+2, "")) } // ReferenceType is a reference type. type ReferenceType struct { Base AST } func (rt *ReferenceType) print(ps *printState) { printBase(ps, rt, rt.Base) } func (rt *ReferenceType) printInner(ps *printState) { ps.writeString("&") } func (rt *ReferenceType) Traverse(fn func(AST) bool) { if fn(rt) { rt.Base.Traverse(fn) } } func (rt *ReferenceType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(rt) { return nil } base := rt.Base.Copy(fn, skip) if base == nil { return fn(rt) } rt = &ReferenceType{Base: base} if r := fn(rt); r != nil { return r } return rt } func (rt *ReferenceType) GoString() string { return rt.goString(0, "") } func (rt *ReferenceType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sReferenceType:\n%s", indent, "", field, rt.Base.goString(indent+2, "")) } // RvalueReferenceType is an rvalue reference type. type RvalueReferenceType struct { Base AST } func (rt *RvalueReferenceType) print(ps *printState) { printBase(ps, rt, rt.Base) } func (rt *RvalueReferenceType) printInner(ps *printState) { ps.writeString("&&") } func (rt *RvalueReferenceType) Traverse(fn func(AST) bool) { if fn(rt) { rt.Base.Traverse(fn) } } func (rt *RvalueReferenceType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(rt) { return nil } base := rt.Base.Copy(fn, skip) if base == nil { return fn(rt) } rt = &RvalueReferenceType{Base: base} if r := fn(rt); r != nil { return r } return rt } func (rt *RvalueReferenceType) GoString() string { return rt.goString(0, "") } func (rt *RvalueReferenceType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sRvalueReferenceType:\n%s", indent, "", field, rt.Base.goString(indent+2, "")) } // ComplexType is a complex type. type ComplexType struct { Base AST } func (ct *ComplexType) print(ps *printState) { printBase(ps, ct, ct.Base) } func (ct *ComplexType) printInner(ps *printState) { ps.writeString(" _Complex") } func (ct *ComplexType) Traverse(fn func(AST) bool) { if fn(ct) { ct.Base.Traverse(fn) } } func (ct *ComplexType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ct) { return nil } base := ct.Base.Copy(fn, skip) if base == nil { return fn(ct) } ct = &ComplexType{Base: base} if r := fn(ct); r != nil { return r } return ct } func (ct *ComplexType) GoString() string { return ct.goString(0, "") } func (ct *ComplexType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sComplexType:\n%s", indent, "", field, ct.Base.goString(indent+2, "")) } // ImaginaryType is an imaginary type. type ImaginaryType struct { Base AST } func (it *ImaginaryType) print(ps *printState) { printBase(ps, it, it.Base) } func (it *ImaginaryType) printInner(ps *printState) { ps.writeString(" _Imaginary") } func (it *ImaginaryType) Traverse(fn func(AST) bool) { if fn(it) { it.Base.Traverse(fn) } } func (it *ImaginaryType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(it) { return nil } base := it.Base.Copy(fn, skip) if base == nil { return fn(it) } it = &ImaginaryType{Base: base} if r := fn(it); r != nil { return r } return it } func (it *ImaginaryType) GoString() string { return it.goString(0, "") } func (it *ImaginaryType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sImaginaryType:\n%s", indent, "", field, it.Base.goString(indent+2, "")) } // SuffixType is an type with an arbitrary suffix. type SuffixType struct { Base AST Suffix string } func (st *SuffixType) print(ps *printState) { printBase(ps, st, st.Base) } func (st *SuffixType) printInner(ps *printState) { ps.writeByte(' ') ps.writeString(st.Suffix) } func (st *SuffixType) Traverse(fn func(AST) bool) { if fn(st) { st.Base.Traverse(fn) } } func (st *SuffixType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(st) { return nil } base := st.Base.Copy(fn, skip) if base == nil { return fn(st) } st = &SuffixType{Base: base, Suffix: st.Suffix} if r := fn(st); r != nil { return r } return st } func (st *SuffixType) GoString() string { return st.goString(0, "") } func (st *SuffixType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sSuffixType: %s\n%s", indent, "", field, st.Suffix, st.Base.goString(indent+2, "Base: ")) } // TransformedType is a builtin type with a template argument. type TransformedType struct { Name string Base AST } func (tt *TransformedType) print(ps *printState) { ps.writeString(tt.Name) ps.startScope('(') ps.print(tt.Base) ps.endScope(')') } func (tt *TransformedType) Traverse(fn func(AST) bool) { if fn(tt) { tt.Base.Traverse(fn) } } func (tt *TransformedType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tt) { return nil } base := tt.Base.Copy(fn, skip) if base == nil { return fn(tt) } tt = &TransformedType{Name: tt.Name, Base: base} if r := fn(tt); r != nil { return r } return tt } func (tt *TransformedType) GoString() string { return tt.goString(0, "") } func (tt *TransformedType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTransformedType: %s\n%s", indent, "", field, tt.Name, tt.Base.goString(indent+2, "Base: ")) } // VendorQualifier is a type qualified by a vendor-specific qualifier. type VendorQualifier struct { Qualifier AST Type AST } func (vq *VendorQualifier) print(ps *printState) { if ps.llvmStyle { ps.print(vq.Type) vq.printInner(ps) } else { ps.inner = append(ps.inner, vq) ps.print(vq.Type) if len(ps.inner) > 0 { ps.printOneInner(nil) } } } func (vq *VendorQualifier) printInner(ps *printState) { ps.writeByte(' ') ps.print(vq.Qualifier) } func (vq *VendorQualifier) Traverse(fn func(AST) bool) { if fn(vq) { vq.Qualifier.Traverse(fn) vq.Type.Traverse(fn) } } func (vq *VendorQualifier) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(vq) { return nil } qualifier := vq.Qualifier.Copy(fn, skip) typ := vq.Type.Copy(fn, skip) if qualifier == nil && typ == nil { return fn(vq) } if qualifier == nil { qualifier = vq.Qualifier } if typ == nil { typ = vq.Type } vq = &VendorQualifier{Qualifier: qualifier, Type: vq.Type} if r := fn(vq); r != nil { return r } return vq } func (vq *VendorQualifier) GoString() string { return vq.goString(0, "") } func (vq *VendorQualifier) goString(indent int, field string) string { return fmt.Sprintf("%*s%sVendorQualifier:\n%s\n%s", indent, "", field, vq.Qualifier.goString(indent+2, "Qualifier: "), vq.Type.goString(indent+2, "Type: ")) } // ArrayType is an array type. type ArrayType struct { Dimension AST Element AST } func (at *ArrayType) print(ps *printState) { // Pass the array type down as an inner type so that we print // multi-dimensional arrays correctly. ps.inner = append(ps.inner, at) ps.print(at.Element) if ln := len(ps.inner); ln > 0 { ps.inner = ps.inner[:ln-1] at.printDimension(ps) } } func (at *ArrayType) printInner(ps *printState) { at.printDimension(ps) } // Print the array dimension. func (at *ArrayType) printDimension(ps *printState) { space := " " for len(ps.inner) > 0 { // We haven't gotten to the real type yet. Use // parentheses around that type, except that if it is // an array type we print it as a multi-dimensional // array in := ps.inner[len(ps.inner)-1] if twq, ok := in.(*TypeWithQualifiers); ok { in = twq.Base } if _, ok := in.(*ArrayType); ok { if in == ps.inner[len(ps.inner)-1] { space = "" } ps.printOneInner(nil) } else { ps.writeByte(' ') ps.startScope('(') ps.printInner(false) ps.endScope(')') } } ps.writeString(space) ps.writeByte('[') ps.print(at.Dimension) ps.writeByte(']') } func (at *ArrayType) Traverse(fn func(AST) bool) { if fn(at) { at.Dimension.Traverse(fn) at.Element.Traverse(fn) } } func (at *ArrayType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(at) { return nil } dimension := at.Dimension.Copy(fn, skip) element := at.Element.Copy(fn, skip) if dimension == nil && element == nil { return fn(at) } if dimension == nil { dimension = at.Dimension } if element == nil { element = at.Element } at = &ArrayType{Dimension: dimension, Element: element} if r := fn(at); r != nil { return r } return at } func (at *ArrayType) GoString() string { return at.goString(0, "") } func (at *ArrayType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sArrayType:\n%s\n%s", indent, "", field, at.Dimension.goString(indent+2, "Dimension: "), at.Element.goString(indent+2, "Element: ")) } // FunctionType is a function type. type FunctionType struct { Return AST Args []AST // The forLocalName field reports whether this FunctionType // was created for a local name. With the default GNU demangling // output we don't print the return type in that case. ForLocalName bool } func (ft *FunctionType) print(ps *printState) { retType := ft.Return if ft.ForLocalName && (!ps.enclosingParams || !ps.llvmStyle) { retType = nil } if retType != nil { // Pass the return type as an inner type in order to // print the arguments in the right location. ps.inner = append(ps.inner, ft) ps.print(retType) if len(ps.inner) == 0 { // Everything was printed. return } ps.inner = ps.inner[:len(ps.inner)-1] ps.writeByte(' ') } ft.printArgs(ps) } func (ft *FunctionType) printInner(ps *printState) { ft.printArgs(ps) } // printArgs prints the arguments of a function type. It looks at the // inner types for spacing. func (ft *FunctionType) printArgs(ps *printState) { paren := false space := false for i := len(ps.inner) - 1; i >= 0; i-- { switch ps.inner[i].(type) { case *PointerType, *ReferenceType, *RvalueReferenceType: paren = true case *TypeWithQualifiers, *ComplexType, *ImaginaryType, *PtrMem: space = true paren = true } if paren { break } } if paren { if !space && (ps.last != '(' && ps.last != '*') { space = true } if space && ps.last != ' ' { ps.writeByte(' ') } ps.startScope('(') } save := ps.printInner(true) if paren { ps.endScope(')') } ps.startScope('(') if !ft.ForLocalName || ps.enclosingParams { first := true for _, a := range ft.Args { if ps.isEmpty(a) { continue } if !first { ps.writeString(", ") } ps.print(a) first = false } } ps.endScope(')') ps.inner = save ps.printInner(false) } func (ft *FunctionType) Traverse(fn func(AST) bool) { if fn(ft) { if ft.Return != nil { ft.Return.Traverse(fn) } for _, a := range ft.Args { a.Traverse(fn) } } } func (ft *FunctionType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ft) { return nil } changed := false var ret AST if ft.Return != nil { ret = ft.Return.Copy(fn, skip) if ret == nil { ret = ft.Return } else { changed = true } } args := make([]AST, len(ft.Args)) for i, a := range ft.Args { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac changed = true } } if !changed { return fn(ft) } ft = &FunctionType{ Return: ret, Args: args, ForLocalName: ft.ForLocalName, } if r := fn(ft); r != nil { return r } return ft } func (ft *FunctionType) GoString() string { return ft.goString(0, "") } func (ft *FunctionType) goString(indent int, field string) string { var forLocalName string if ft.ForLocalName { forLocalName = " ForLocalName: true" } var r string if ft.Return == nil { r = fmt.Sprintf("%*sReturn: nil", indent+2, "") } else { r = ft.Return.goString(indent+2, "Return: ") } var args string if len(ft.Args) == 0 { args = fmt.Sprintf("%*sArgs: nil", indent+2, "") } else { args = fmt.Sprintf("%*sArgs:", indent+2, "") for i, a := range ft.Args { args += "\n" args += a.goString(indent+4, fmt.Sprintf("%d: ", i)) } } return fmt.Sprintf("%*s%sFunctionType:%s\n%s\n%s", indent, "", field, forLocalName, r, args) } // FunctionParam is a parameter of a function, used for last-specified // return type in a closure. type FunctionParam struct { Index int } func (fp *FunctionParam) print(ps *printState) { if fp.Index == 0 { ps.writeString("this") } else if ps.llvmStyle { if fp.Index == 1 { ps.writeString("fp") } else { fmt.Fprintf(&ps.buf, "fp%d", fp.Index-2) } } else { fmt.Fprintf(&ps.buf, "{parm#%d}", fp.Index) } } func (fp *FunctionParam) Traverse(fn func(AST) bool) { fn(fp) } func (fp *FunctionParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(fp) { return nil } return fn(fp) } func (fp *FunctionParam) GoString() string { return fp.goString(0, "") } func (fp *FunctionParam) goString(indent int, field string) string { return fmt.Sprintf("%*s%sFunctionParam: %d", indent, "", field, fp.Index) } func (fp *FunctionParam) prec() precedence { return precPrimary } // PtrMem is a pointer-to-member expression. type PtrMem struct { Class AST Member AST } func (pm *PtrMem) print(ps *printState) { ps.inner = append(ps.inner, pm) ps.print(pm.Member) if len(ps.inner) > 0 { ps.printOneInner(nil) } } func (pm *PtrMem) printInner(ps *printState) { if ps.last != '(' { ps.writeByte(' ') } ps.print(pm.Class) ps.writeString("::*") } func (pm *PtrMem) Traverse(fn func(AST) bool) { if fn(pm) { pm.Class.Traverse(fn) pm.Member.Traverse(fn) } } func (pm *PtrMem) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(pm) { return nil } class := pm.Class.Copy(fn, skip) member := pm.Member.Copy(fn, skip) if class == nil && member == nil { return fn(pm) } if class == nil { class = pm.Class } if member == nil { member = pm.Member } pm = &PtrMem{Class: class, Member: member} if r := fn(pm); r != nil { return r } return pm } func (pm *PtrMem) GoString() string { return pm.goString(0, "") } func (pm *PtrMem) goString(indent int, field string) string { return fmt.Sprintf("%*s%sPtrMem:\n%s\n%s", indent, "", field, pm.Class.goString(indent+2, "Class: "), pm.Member.goString(indent+2, "Member: ")) } // FixedType is a fixed numeric type of unknown size. type FixedType struct { Base AST Accum bool Sat bool } func (ft *FixedType) print(ps *printState) { if ft.Sat { ps.writeString("_Sat ") } if bt, ok := ft.Base.(*BuiltinType); ok && bt.Name == "int" { // The standard demangler skips printing "int". } else { ps.print(ft.Base) ps.writeByte(' ') } if ft.Accum { ps.writeString("_Accum") } else { ps.writeString("_Fract") } } func (ft *FixedType) Traverse(fn func(AST) bool) { if fn(ft) { ft.Base.Traverse(fn) } } func (ft *FixedType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ft) { return nil } base := ft.Base.Copy(fn, skip) if base == nil { return fn(ft) } ft = &FixedType{Base: base, Accum: ft.Accum, Sat: ft.Sat} if r := fn(ft); r != nil { return r } return ft } func (ft *FixedType) GoString() string { return ft.goString(0, "") } func (ft *FixedType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sFixedType: Accum: %t; Sat: %t\n%s", indent, "", field, ft.Accum, ft.Sat, ft.Base.goString(indent+2, "Base: ")) } // BinaryFP is a binary floating-point type. type BinaryFP struct { Bits int } func (bfp *BinaryFP) print(ps *printState) { fmt.Fprintf(&ps.buf, "_Float%d", bfp.Bits) } func (bfp *BinaryFP) Traverse(fn func(AST) bool) { fn(bfp) } func (bfp *BinaryFP) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(bfp) { return nil } return fn(bfp) } func (bfp *BinaryFP) GoString() string { return bfp.goString(0, "") } func (bfp *BinaryFP) goString(indent int, field string) string { return fmt.Sprintf("%*s%sBinaryFP: %d", indent, "", field, bfp.Bits) } // BitIntType is the C++23 _BitInt(N) type. type BitIntType struct { Size AST Signed bool } func (bt *BitIntType) print(ps *printState) { if !bt.Signed { ps.writeString("unsigned ") } ps.writeString("_BitInt") ps.startScope('(') ps.print(bt.Size) ps.endScope(')') } func (bt *BitIntType) Traverse(fn func(AST) bool) { if fn(bt) { bt.Size.Traverse(fn) } } func (bt *BitIntType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(bt) { return nil } size := bt.Size.Copy(fn, skip) if size == nil { return fn(bt) } bt = &BitIntType{Size: size, Signed: bt.Signed} if r := fn(bt); r != nil { return r } return bt } func (bt *BitIntType) GoString() string { return bt.goString(0, "") } func (bt *BitIntType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sBitIntType: Signed: %t\n%s", indent, "", field, bt.Signed, bt.Size.goString(indent+2, "Size: ")) } // VectorType is a vector type. type VectorType struct { Dimension AST Base AST } func (vt *VectorType) print(ps *printState) { ps.inner = append(ps.inner, vt) ps.print(vt.Base) if len(ps.inner) > 0 { ps.printOneInner(nil) } } func (vt *VectorType) printInner(ps *printState) { end := byte(')') if ps.llvmStyle { ps.writeString(" vector[") end = ']' } else { ps.writeString(" __vector(") } ps.print(vt.Dimension) ps.writeByte(end) } func (vt *VectorType) Traverse(fn func(AST) bool) { if fn(vt) { vt.Dimension.Traverse(fn) vt.Base.Traverse(fn) } } func (vt *VectorType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(vt) { return nil } dimension := vt.Dimension.Copy(fn, skip) base := vt.Base.Copy(fn, skip) if dimension == nil && base == nil { return fn(vt) } if dimension == nil { dimension = vt.Dimension } if base == nil { base = vt.Base } vt = &VectorType{Dimension: dimension, Base: base} if r := fn(vt); r != nil { return r } return vt } func (vt *VectorType) GoString() string { return vt.goString(0, "") } func (vt *VectorType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sVectorType:\n%s\n%s", indent, "", field, vt.Dimension.goString(indent+2, "Dimension: "), vt.Base.goString(indent+2, "Base: ")) } // ElaboratedType is an elaborated struct/union/enum type. type ElaboratedType struct { Kind string Type AST } func (et *ElaboratedType) print(ps *printState) { ps.writeString(et.Kind) ps.writeString(" ") et.Type.print(ps) } func (et *ElaboratedType) Traverse(fn func(AST) bool) { if fn(et) { et.Type.Traverse(fn) } } func (et *ElaboratedType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(et) { return nil } typ := et.Type.Copy(fn, skip) if typ == nil { return fn(et) } et = &ElaboratedType{Kind: et.Kind, Type: typ} if r := fn(et); r != nil { return r } return et } func (et *ElaboratedType) GoString() string { return et.goString(0, "") } func (et *ElaboratedType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sElaboratedtype: Kind: %s\n%s", indent, "", field, et.Kind, et.Type.goString(indent+2, "Expr: ")) } // Decltype is the decltype operator. type Decltype struct { Expr AST } func (dt *Decltype) print(ps *printState) { ps.writeString("decltype") if !ps.llvmStyle { ps.writeString(" ") } ps.startScope('(') ps.print(dt.Expr) ps.endScope(')') } func (dt *Decltype) Traverse(fn func(AST) bool) { if fn(dt) { dt.Expr.Traverse(fn) } } func (dt *Decltype) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(dt) { return nil } expr := dt.Expr.Copy(fn, skip) if expr == nil { return fn(dt) } dt = &Decltype{Expr: expr} if r := fn(dt); r != nil { return r } return dt } func (dt *Decltype) GoString() string { return dt.goString(0, "") } func (dt *Decltype) goString(indent int, field string) string { return fmt.Sprintf("%*s%sDecltype:\n%s", indent, "", field, dt.Expr.goString(indent+2, "Expr: ")) } // Operator is an operator. type Operator struct { Name string precedence precedence } func (op *Operator) print(ps *printState) { ps.writeString("operator") if isLower(op.Name[0]) { ps.writeByte(' ') } n := op.Name n = strings.TrimSuffix(n, " ") ps.writeString(n) } func (op *Operator) Traverse(fn func(AST) bool) { fn(op) } func (op *Operator) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(op) { return nil } return fn(op) } func (op *Operator) GoString() string { return op.goString(0, "") } func (op *Operator) goString(indent int, field string) string { return fmt.Sprintf("%*s%sOperator: %s", indent, "", field, op.Name) } func (op *Operator) prec() precedence { return op.precedence } // Constructor is a constructor. type Constructor struct { Name AST Base AST // base class of inheriting constructor } func (c *Constructor) print(ps *printState) { ps.print(c.Name) // We don't include the base class in the demangled string. } func (c *Constructor) Traverse(fn func(AST) bool) { if fn(c) { c.Name.Traverse(fn) if c.Base != nil { c.Base.Traverse(fn) } } } func (c *Constructor) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(c) { return nil } name := c.Name.Copy(fn, skip) var base AST if c.Base != nil { base = c.Base.Copy(fn, skip) } if name == nil && base == nil { return fn(c) } if name == nil { name = c.Name } if base == nil { base = c.Base } c = &Constructor{Name: name, Base: base} if r := fn(c); r != nil { return r } return c } func (c *Constructor) GoString() string { return c.goString(0, "") } func (c *Constructor) goString(indent int, field string) string { var sb strings.Builder fmt.Fprintf(&sb, "%*s%sConstructor:\n", indent, "", field) if c.Base != nil { fmt.Fprintf(&sb, "%s\n", c.Base.goString(indent+2, "Base: ")) } fmt.Fprintf(&sb, "%s", c.Name.goString(indent+2, "Name: ")) return sb.String() } // Destructor is a destructor. type Destructor struct { Name AST } func (d *Destructor) print(ps *printState) { ps.writeByte('~') ps.print(d.Name) } func (d *Destructor) Traverse(fn func(AST) bool) { if fn(d) { d.Name.Traverse(fn) } } func (d *Destructor) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(d) { return nil } name := d.Name.Copy(fn, skip) if name == nil { return fn(d) } d = &Destructor{Name: name} if r := fn(d); r != nil { return r } return d } func (d *Destructor) GoString() string { return d.goString(0, "") } func (d *Destructor) goString(indent int, field string) string { return fmt.Sprintf("%*s%sDestructor:\n%s", indent, "", field, d.Name.goString(indent+2, "Name: ")) } // GlobalCDtor is a global constructor or destructor. type GlobalCDtor struct { Ctor bool Key AST } func (gcd *GlobalCDtor) print(ps *printState) { ps.writeString("global ") if gcd.Ctor { ps.writeString("constructors") } else { ps.writeString("destructors") } ps.writeString(" keyed to ") ps.print(gcd.Key) } func (gcd *GlobalCDtor) Traverse(fn func(AST) bool) { if fn(gcd) { gcd.Key.Traverse(fn) } } func (gcd *GlobalCDtor) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(gcd) { return nil } key := gcd.Key.Copy(fn, skip) if key == nil { return fn(gcd) } gcd = &GlobalCDtor{Ctor: gcd.Ctor, Key: key} if r := fn(gcd); r != nil { return r } return gcd } func (gcd *GlobalCDtor) GoString() string { return gcd.goString(0, "") } func (gcd *GlobalCDtor) goString(indent int, field string) string { return fmt.Sprintf("%*s%sGlobalCDtor: Ctor: %t\n%s", indent, "", field, gcd.Ctor, gcd.Key.goString(indent+2, "Key: ")) } // TaggedName is a name with an ABI tag. type TaggedName struct { Name AST Tag AST } func (t *TaggedName) print(ps *printState) { ps.print(t.Name) ps.writeString("[abi:") ps.print(t.Tag) ps.writeByte(']') } func (t *TaggedName) Traverse(fn func(AST) bool) { if fn(t) { t.Name.Traverse(fn) t.Tag.Traverse(fn) } } func (t *TaggedName) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(t) { return nil } name := t.Name.Copy(fn, skip) tag := t.Tag.Copy(fn, skip) if name == nil && tag == nil { return fn(t) } if name == nil { name = t.Name } if tag == nil { tag = t.Tag } t = &TaggedName{Name: name, Tag: tag} if r := fn(t); r != nil { return r } return t } func (t *TaggedName) GoString() string { return t.goString(0, "") } func (t *TaggedName) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTaggedName:\n%s\n%s", indent, "", field, t.Name.goString(indent+2, "Name: "), t.Tag.goString(indent+2, "Tag: ")) } // PackExpansion is a pack expansion. The Pack field may be nil. type PackExpansion struct { Base AST Pack *ArgumentPack } func (pe *PackExpansion) print(ps *printState) { // We normally only get here if the simplify function was // unable to locate and expand the pack. if pe.Pack == nil { if ps.llvmStyle { ps.print(pe.Base) } else { parenthesize(ps, pe.Base) ps.writeString("...") } } else { ps.print(pe.Base) } } func (pe *PackExpansion) Traverse(fn func(AST) bool) { if fn(pe) { pe.Base.Traverse(fn) // Don't traverse Template--it points elsewhere in the AST. } } func (pe *PackExpansion) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(pe) { return nil } base := pe.Base.Copy(fn, skip) if base == nil { return fn(pe) } pe = &PackExpansion{Base: base, Pack: pe.Pack} if r := fn(pe); r != nil { return r } return pe } func (pe *PackExpansion) GoString() string { return pe.goString(0, "") } func (pe *PackExpansion) goString(indent int, field string) string { return fmt.Sprintf("%*s%sPackExpansion: Pack: %p\n%s", indent, "", field, pe.Pack, pe.Base.goString(indent+2, "Base: ")) } // ArgumentPack is an argument pack. type ArgumentPack struct { Args []AST } func (ap *ArgumentPack) print(ps *printState) { for i, a := range ap.Args { if i > 0 { ps.writeString(", ") } ps.print(a) } } func (ap *ArgumentPack) Traverse(fn func(AST) bool) { if fn(ap) { for _, a := range ap.Args { a.Traverse(fn) } } } func (ap *ArgumentPack) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ap) { return nil } args := make([]AST, len(ap.Args)) changed := false for i, a := range ap.Args { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac changed = true } } if !changed { return fn(ap) } ap = &ArgumentPack{Args: args} if r := fn(ap); r != nil { return r } return ap } func (ap *ArgumentPack) GoString() string { return ap.goString(0, "") } func (ap *ArgumentPack) goString(indent int, field string) string { if len(ap.Args) == 0 { return fmt.Sprintf("%*s%sArgumentPack: nil", indent, "", field) } s := fmt.Sprintf("%*s%sArgumentPack:", indent, "", field) for i, a := range ap.Args { s += "\n" s += a.goString(indent+2, fmt.Sprintf("%d: ", i)) } return s } // SizeofPack is the sizeof operator applied to an argument pack. type SizeofPack struct { Pack *ArgumentPack } func (sp *SizeofPack) print(ps *printState) { if ps.llvmStyle { ps.writeString("sizeof...") ps.startScope('(') ps.print(sp.Pack) ps.endScope(')') } else { ps.writeString(fmt.Sprintf("%d", len(sp.Pack.Args))) } } func (sp *SizeofPack) Traverse(fn func(AST) bool) { fn(sp) // Don't traverse the pack--it points elsewhere in the AST. } func (sp *SizeofPack) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(sp) { return nil } sp = &SizeofPack{Pack: sp.Pack} if r := fn(sp); r != nil { return r } return sp } func (sp *SizeofPack) GoString() string { return sp.goString(0, "") } func (sp *SizeofPack) goString(indent int, field string) string { return fmt.Sprintf("%*s%sSizeofPack: Pack: %p", indent, "", field, sp.Pack) } // SizeofArgs is the size of a captured template parameter pack from // an alias template. type SizeofArgs struct { Args []AST } func (sa *SizeofArgs) print(ps *printState) { c := 0 for _, a := range sa.Args { if ap, ok := a.(*ArgumentPack); ok { c += len(ap.Args) } else if el, ok := a.(*ExprList); ok { c += len(el.Exprs) } else { c++ } } ps.writeString(fmt.Sprintf("%d", c)) } func (sa *SizeofArgs) Traverse(fn func(AST) bool) { if fn(sa) { for _, a := range sa.Args { a.Traverse(fn) } } } func (sa *SizeofArgs) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(sa) { return nil } changed := false args := make([]AST, len(sa.Args)) for i, a := range sa.Args { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac changed = true } } if !changed { return fn(sa) } sa = &SizeofArgs{Args: args} if r := fn(sa); r != nil { return r } return sa } func (sa *SizeofArgs) GoString() string { return sa.goString(0, "") } func (sa *SizeofArgs) goString(indent int, field string) string { var args string if len(sa.Args) == 0 { args = fmt.Sprintf("%*sArgs: nil", indent+2, "") } else { args = fmt.Sprintf("%*sArgs:", indent+2, "") for i, a := range sa.Args { args += "\n" args += a.goString(indent+4, fmt.Sprintf("%d: ", i)) } } return fmt.Sprintf("%*s%sSizeofArgs:\n%s", indent, "", field, args) } // TemplateParamName is the name of a template parameter that the // demangler introduced for a lambda that has explicit template // parameters. This is a prefix with an index. type TemplateParamName struct { Prefix string Index int } func (tpn *TemplateParamName) print(ps *printState) { ps.writeString(tpn.Prefix) if tpn.Index > 0 { ps.writeString(fmt.Sprintf("%d", tpn.Index-1)) } } func (tpn *TemplateParamName) Traverse(fn func(AST) bool) { fn(tpn) } func (tpn *TemplateParamName) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tpn) { return nil } return fn(tpn) } func (tpn *TemplateParamName) GoString() string { return tpn.goString(0, "") } func (tpn *TemplateParamName) goString(indent int, field string) string { name := tpn.Prefix if tpn.Index > 0 { name += fmt.Sprintf("%d", tpn.Index-1) } return fmt.Sprintf("%*s%sTemplateParamName: %s", indent, "", field, name) } // TypeTemplateParam is a type template parameter that appears in a // lambda with explicit template parameters. type TypeTemplateParam struct { Name AST } func (ttp *TypeTemplateParam) print(ps *printState) { ps.writeString("typename ") ps.printInner(false) ps.print(ttp.Name) } func (ttp *TypeTemplateParam) Traverse(fn func(AST) bool) { if fn(ttp) { ttp.Name.Traverse(fn) } } func (ttp *TypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ttp) { return nil } name := ttp.Name.Copy(fn, skip) if name == nil { return fn(ttp) } ttp = &TypeTemplateParam{Name: name} if r := fn(ttp); r != nil { return r } return ttp } func (ttp *TypeTemplateParam) GoString() string { return ttp.goString(0, "") } func (ttp *TypeTemplateParam) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTypeTemplateParam:\n%s", indent, "", field, ttp.Name.goString(indent+2, "Name")) } // NonTypeTemplateParam is a non-type template parameter that appears // in a lambda with explicit template parameters. type NonTypeTemplateParam struct { Name AST Type AST } func (nttp *NonTypeTemplateParam) print(ps *printState) { ps.inner = append(ps.inner, nttp) ps.print(nttp.Type) if len(ps.inner) > 0 { ps.writeByte(' ') ps.print(nttp.Name) ps.inner = ps.inner[:len(ps.inner)-1] } } func (nttp *NonTypeTemplateParam) printInner(ps *printState) { ps.print(nttp.Name) } func (nttp *NonTypeTemplateParam) Traverse(fn func(AST) bool) { if fn(nttp) { nttp.Name.Traverse(fn) nttp.Type.Traverse(fn) } } func (nttp *NonTypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(nttp) { return nil } name := nttp.Name.Copy(fn, skip) typ := nttp.Type.Copy(fn, skip) if name == nil && typ == nil { return fn(nttp) } if name == nil { name = nttp.Name } if typ == nil { typ = nttp.Type } nttp = &NonTypeTemplateParam{Name: name, Type: typ} if r := fn(nttp); r != nil { return r } return nttp } func (nttp *NonTypeTemplateParam) GoString() string { return nttp.goString(0, "") } func (nttp *NonTypeTemplateParam) goString(indent int, field string) string { return fmt.Sprintf("%*s%sNonTypeTemplateParam:\n%s\n%s", indent, "", field, nttp.Name.goString(indent+2, "Name: "), nttp.Type.goString(indent+2, "Type: ")) } // TemplateTemplateParam is a template template parameter that appears // in a lambda with explicit template parameters. type TemplateTemplateParam struct { Name AST Params []AST Constraint AST } func (ttp *TemplateTemplateParam) print(ps *printState) { scopes := ps.scopes ps.scopes = 0 ps.writeString("template<") ps.printList(ttp.Params, nil) ps.writeString("> typename ") ps.scopes = scopes ps.print(ttp.Name) if ttp.Constraint != nil { ps.writeString(" requires ") ps.print(ttp.Constraint) } } func (ttp *TemplateTemplateParam) Traverse(fn func(AST) bool) { if fn(ttp) { ttp.Name.Traverse(fn) for _, param := range ttp.Params { param.Traverse(fn) } if ttp.Constraint != nil { ttp.Constraint.Traverse(fn) } } } func (ttp *TemplateTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ttp) { return nil } changed := false name := ttp.Name.Copy(fn, skip) if name == nil { name = ttp.Name } else { changed = true } params := make([]AST, len(ttp.Params)) for i, p := range ttp.Params { pc := p.Copy(fn, skip) if pc == nil { params[i] = p } else { params[i] = pc changed = true } } var constraint AST if ttp.Constraint != nil { constraint = ttp.Constraint.Copy(fn, skip) if constraint == nil { constraint = ttp.Constraint } else { changed = true } } if !changed { return fn(ttp) } ttp = &TemplateTemplateParam{ Name: name, Params: params, Constraint: constraint, } if r := fn(ttp); r != nil { return r } return ttp } func (ttp *TemplateTemplateParam) GoString() string { return ttp.goString(0, "") } func (ttp *TemplateTemplateParam) goString(indent int, field string) string { var params strings.Builder fmt.Fprintf(¶ms, "%*sParams:", indent+2, "") for i, p := range ttp.Params { params.WriteByte('\n') params.WriteString(p.goString(indent+4, fmt.Sprintf("%d: ", i))) } var constraint string if ttp.Constraint == nil { constraint = fmt.Sprintf("%*sConstraint: nil", indent+2, "") } else { constraint = ttp.Constraint.goString(indent+2, "Constraint: ") } return fmt.Sprintf("%*s%sTemplateTemplateParam:\n%s\n%s\n%s", indent, "", field, ttp.Name.goString(indent+2, "Name: "), params.String(), constraint) } // ConstrainedTypeTemplateParam is a constrained template type // parameter declaration. type ConstrainedTypeTemplateParam struct { Name AST Constraint AST } func (cttp *ConstrainedTypeTemplateParam) print(ps *printState) { ps.inner = append(ps.inner, cttp) ps.print(cttp.Constraint) if len(ps.inner) > 0 { ps.writeByte(' ') ps.print(cttp.Name) ps.inner = ps.inner[:len(ps.inner)-1] } } func (cttp *ConstrainedTypeTemplateParam) printInner(ps *printState) { ps.print(cttp.Name) } func (cttp *ConstrainedTypeTemplateParam) Traverse(fn func(AST) bool) { if fn(cttp) { cttp.Name.Traverse(fn) cttp.Constraint.Traverse(fn) } } func (cttp *ConstrainedTypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(cttp) { return nil } name := cttp.Name.Copy(fn, skip) constraint := cttp.Constraint.Copy(fn, skip) if name == nil && constraint == nil { return fn(cttp) } if name == nil { name = cttp.Name } if constraint == nil { constraint = cttp.Constraint } cttp = &ConstrainedTypeTemplateParam{Name: name, Constraint: constraint} if r := fn(cttp); r != nil { return r } return cttp } func (cttp *ConstrainedTypeTemplateParam) GoString() string { return cttp.goString(0, "") } func (cttp *ConstrainedTypeTemplateParam) goString(indent int, field string) string { return fmt.Sprintf("%*s%sConstrainedTypeTemplateParam\n%s\n%s", indent, "", field, cttp.Name.goString(indent+2, "Name: "), cttp.Constraint.goString(indent+2, "Constraint: ")) } // TemplateParamPack is a template parameter pack that appears in a // lambda with explicit template parameters. type TemplateParamPack struct { Param AST } func (tpp *TemplateParamPack) print(ps *printState) { holdInner := ps.inner defer func() { ps.inner = holdInner }() ps.inner = []AST{tpp} if nttp, ok := tpp.Param.(*NonTypeTemplateParam); ok { ps.print(nttp.Type) } else { ps.print(tpp.Param) } if len(ps.inner) > 0 { ps.writeString("...") } } func (tpp *TemplateParamPack) printInner(ps *printState) { ps.writeString("...") if nttp, ok := tpp.Param.(*NonTypeTemplateParam); ok { ps.print(nttp.Name) } } func (tpp *TemplateParamPack) Traverse(fn func(AST) bool) { if fn(tpp) { tpp.Param.Traverse(fn) } } func (tpp *TemplateParamPack) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tpp) { return nil } param := tpp.Param.Copy(fn, skip) if param == nil { return fn(tpp) } tpp = &TemplateParamPack{Param: param} if r := fn(tpp); r != nil { return r } return tpp } func (tpp *TemplateParamPack) GoString() string { return tpp.goString(0, "") } func (tpp *TemplateParamPack) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTemplateParamPack:\n%s", indent, "", field, tpp.Param.goString(indent+2, "Param: ")) } // Cast is a type cast. type Cast struct { To AST } func (c *Cast) print(ps *printState) { ps.writeString("operator ") ps.print(c.To) } func (c *Cast) Traverse(fn func(AST) bool) { if fn(c) { c.To.Traverse(fn) } } func (c *Cast) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(c) { return nil } to := c.To.Copy(fn, skip) if to == nil { return fn(c) } c = &Cast{To: to} if r := fn(c); r != nil { return r } return c } func (c *Cast) GoString() string { return c.goString(0, "") } func (c *Cast) goString(indent int, field string) string { return fmt.Sprintf("%*s%sCast\n%s", indent, "", field, c.To.goString(indent+2, "To: ")) } func (c *Cast) prec() precedence { return precCast } // The parenthesize function prints the string for val, wrapped in // parentheses if necessary. func parenthesize(ps *printState, val AST) { paren := false switch v := val.(type) { case *Name, *InitializerList: case *FunctionParam: if ps.llvmStyle { paren = true } case *Qualified: if v.LocalName { paren = true } default: paren = true } if paren { ps.startScope('(') } ps.print(val) if paren { ps.endScope(')') } } // Nullary is an operator in an expression with no arguments, such as // throw. type Nullary struct { Op AST } func (n *Nullary) print(ps *printState) { if op, ok := n.Op.(*Operator); ok { ps.writeString(op.Name) } else { ps.print(n.Op) } } func (n *Nullary) Traverse(fn func(AST) bool) { if fn(n) { n.Op.Traverse(fn) } } func (n *Nullary) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(n) { return nil } op := n.Op.Copy(fn, skip) if op == nil { return fn(n) } n = &Nullary{Op: op} if r := fn(n); r != nil { return r } return n } func (n *Nullary) GoString() string { return n.goString(0, "") } func (n *Nullary) goString(indent int, field string) string { return fmt.Sprintf("%*s%sNullary:\n%s", indent, "", field, n.Op.goString(indent+2, "Op: ")) } // Unary is a unary operation in an expression. type Unary struct { Op AST Expr AST Suffix bool // true for ++ -- when used as postfix SizeofType bool // true for sizeof (type) } func (u *Unary) print(ps *printState) { op, _ := u.Op.(*Operator) expr := u.Expr // Don't print the argument list when taking the address of a // function. if !ps.llvmStyle { if op != nil && op.Name == "&" { if t, ok := expr.(*Typed); ok { if _, ok := t.Type.(*FunctionType); ok { expr = t.Name } } } } if u.Suffix { if ps.llvmStyle { wantParens := true opPrec := precUnary if op != nil { opPrec = op.precedence } if p, ok := expr.(hasPrec); ok { if p.prec() < opPrec { wantParens = false } } if wantParens { ps.startScope('(') } ps.print(expr) if wantParens { ps.endScope(')') } } else { parenthesize(ps, expr) } } if op != nil { ps.writeString(op.Name) if ps.llvmStyle && op.Name == "noexcept" { ps.writeByte(' ') } } else if c, ok := u.Op.(*Cast); ok { ps.startScope('(') ps.print(c.To) ps.endScope(')') } else { ps.print(u.Op) } if !u.Suffix { isDelete := op != nil && (op.Name == "delete " || op.Name == "delete[] ") if op != nil && op.Name == "::" { // Don't use parentheses after ::. ps.print(expr) } else if u.SizeofType { // Always use parentheses for sizeof argument. ps.startScope('(') ps.print(expr) ps.endScope(')') } else if op != nil && op.Name == "__alignof__" { // Always use parentheses for __alignof__ argument. ps.startScope('(') ps.print(expr) ps.endScope(')') } else if ps.llvmStyle { var wantParens bool switch { case op == nil: wantParens = true case op.Name == `operator"" `: wantParens = false case op.Name == "&": wantParens = false case isDelete: wantParens = false case op.Name == "alignof ": wantParens = true case op.Name == "sizeof ": wantParens = true case op.Name == "typeid ": wantParens = true default: wantParens = true if p, ok := expr.(hasPrec); ok { if p.prec() < op.precedence { wantParens = false } } } if wantParens { ps.startScope('(') } ps.print(expr) if wantParens { ps.endScope(')') } } else { parenthesize(ps, expr) } } } func (u *Unary) Traverse(fn func(AST) bool) { if fn(u) { u.Op.Traverse(fn) u.Expr.Traverse(fn) } } func (u *Unary) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(u) { return nil } op := u.Op.Copy(fn, skip) expr := u.Expr.Copy(fn, skip) if op == nil && expr == nil { return fn(u) } if op == nil { op = u.Op } if expr == nil { expr = u.Expr } u = &Unary{Op: op, Expr: expr, Suffix: u.Suffix, SizeofType: u.SizeofType} if r := fn(u); r != nil { return r } return u } func (u *Unary) GoString() string { return u.goString(0, "") } func (u *Unary) goString(indent int, field string) string { var s string if u.Suffix { s = " Suffix: true" } if u.SizeofType { s += " SizeofType: true" } return fmt.Sprintf("%*s%sUnary:%s\n%s\n%s", indent, "", field, s, u.Op.goString(indent+2, "Op: "), u.Expr.goString(indent+2, "Expr: ")) } func (u *Unary) prec() precedence { if p, ok := u.Op.(hasPrec); ok { return p.prec() } return precDefault } // isDesignatedInitializer reports whether x is a designated // initializer. func isDesignatedInitializer(x AST) bool { switch x := x.(type) { case *Binary: if op, ok := x.Op.(*Operator); ok { if op.Name == "]=" { return true } if op.Name != "=" { return false } if _, ok := x.Left.(*Literal); ok { return false } return true } case *Trinary: if op, ok := x.Op.(*Operator); ok { return op.Name == "[...]=" } } return false } // Binary is a binary operation in an expression. type Binary struct { Op AST Left AST Right AST } func (b *Binary) print(ps *printState) { op, _ := b.Op.(*Operator) if op != nil && strings.Contains(op.Name, "cast") { ps.writeString(op.Name) scopes := ps.scopes ps.scopes = 0 ps.writeByte('<') ps.print(b.Left) ps.writeString(">") ps.scopes = scopes ps.startScope('(') ps.print(b.Right) ps.endScope(')') return } if isDesignatedInitializer(b) { if op.Name == "=" { ps.writeByte('.') } else { ps.writeByte('[') } ps.print(b.Left) if op.Name == "]=" { ps.writeByte(']') } if isDesignatedInitializer(b.Right) { // Don't add anything between designated // initializer chains. ps.print(b.Right) } else { if ps.llvmStyle { ps.writeString(" = ") ps.print(b.Right) } else { ps.writeByte('=') parenthesize(ps, b.Right) } } return } // Use an extra set of parentheses around an expression that // uses the greater-than operator, so that it does not get // confused with the '>' that ends template parameters. needsOuterParen := op != nil && (op.Name == ">" || op.Name == ">>") if ps.llvmStyle && ps.scopes > 0 { needsOuterParen = false } if needsOuterParen { ps.startScope('(') } left := b.Left skipParens := false addSpaces := ps.llvmStyle if ps.llvmStyle && op != nil { switch op.Name { case ".", "->", "->*": addSpaces = false } } // For a function call in an expression, don't print the types // of the arguments unless there is a return type. if op != nil && op.Name == "()" { if ty, ok := b.Left.(*Typed); ok { if ft, ok := ty.Type.(*FunctionType); ok { if ft.Return == nil { left = ty.Name } else { skipParens = true } } else { left = ty.Name } } if ps.llvmStyle { skipParens = true } } if skipParens { ps.print(left) } else if ps.llvmStyle { prec := precPrimary if p, ok := left.(hasPrec); ok { prec = p.prec() } needsParen := false if prec > b.prec() { needsParen = true } if needsParen { ps.startScope('(') } ps.print(left) if needsParen { ps.endScope(')') } } else { parenthesize(ps, left) } if op != nil && op.Name == "[]" { ps.writeByte('[') ps.print(b.Right) ps.writeByte(']') return } if op != nil { if op.Name != "()" { if addSpaces && op.Name != "," { ps.writeByte(' ') } ps.writeString(op.Name) if addSpaces { ps.writeByte(' ') } } } else { ps.print(b.Op) } if ps.llvmStyle { prec := precPrimary if p, ok := b.Right.(hasPrec); ok { prec = p.prec() } needsParen := false if prec >= b.prec() { needsParen = true } if needsParen { ps.startScope('(') } ps.print(b.Right) if needsParen { ps.endScope(')') } } else { parenthesize(ps, b.Right) } if needsOuterParen { ps.endScope(')') } } func (b *Binary) Traverse(fn func(AST) bool) { if fn(b) { b.Op.Traverse(fn) b.Left.Traverse(fn) b.Right.Traverse(fn) } } func (b *Binary) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(b) { return nil } op := b.Op.Copy(fn, skip) left := b.Left.Copy(fn, skip) right := b.Right.Copy(fn, skip) if op == nil && left == nil && right == nil { return fn(b) } if op == nil { op = b.Op } if left == nil { left = b.Left } if right == nil { right = b.Right } b = &Binary{Op: op, Left: left, Right: right} if r := fn(b); r != nil { return r } return b } func (b *Binary) GoString() string { return b.goString(0, "") } func (b *Binary) goString(indent int, field string) string { return fmt.Sprintf("%*s%sBinary:\n%s\n%s\n%s", indent, "", field, b.Op.goString(indent+2, "Op: "), b.Left.goString(indent+2, "Left: "), b.Right.goString(indent+2, "Right: ")) } func (b *Binary) prec() precedence { if p, ok := b.Op.(hasPrec); ok { return p.prec() } return precDefault } // Trinary is the ?: trinary operation in an expression. type Trinary struct { Op AST First AST Second AST Third AST } func (t *Trinary) print(ps *printState) { if isDesignatedInitializer(t) { ps.writeByte('[') ps.print(t.First) ps.writeString(" ... ") ps.print(t.Second) ps.writeByte(']') if isDesignatedInitializer(t.Third) { // Don't add anything between designated // initializer chains. ps.print(t.Third) } else { if ps.llvmStyle { ps.writeString(" = ") ps.print(t.Third) } else { ps.writeByte('=') parenthesize(ps, t.Third) } } return } if ps.llvmStyle { wantParens := true opPrec := precPrimary if op, ok := t.Op.(*Operator); ok { opPrec = op.precedence } if p, ok := t.First.(hasPrec); ok { if p.prec() < opPrec { wantParens = false } } if wantParens { ps.startScope('(') } ps.print(t.First) if wantParens { ps.endScope(')') } } else { parenthesize(ps, t.First) } if ps.llvmStyle { ps.writeString(" ? ") } else { ps.writeByte('?') } if ps.llvmStyle { wantParens := true if p, ok := t.Second.(hasPrec); ok { if p.prec() < precDefault { wantParens = false } } if wantParens { ps.startScope('(') } ps.print(t.Second) if wantParens { ps.endScope(')') } } else { parenthesize(ps, t.Second) } ps.writeString(" : ") if ps.llvmStyle { wantParens := true if p, ok := t.Third.(hasPrec); ok { if p.prec() < precAssign { wantParens = false } } if wantParens { ps.startScope('(') } ps.print(t.Third) if wantParens { ps.endScope(')') } } else { parenthesize(ps, t.Third) } } func (t *Trinary) Traverse(fn func(AST) bool) { if fn(t) { t.Op.Traverse(fn) t.First.Traverse(fn) t.Second.Traverse(fn) t.Third.Traverse(fn) } } func (t *Trinary) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(t) { return nil } op := t.Op.Copy(fn, skip) first := t.First.Copy(fn, skip) second := t.Second.Copy(fn, skip) third := t.Third.Copy(fn, skip) if op == nil && first == nil && second == nil && third == nil { return fn(t) } if op == nil { op = t.Op } if first == nil { first = t.First } if second == nil { second = t.Second } if third == nil { third = t.Third } t = &Trinary{Op: op, First: first, Second: second, Third: third} if r := fn(t); r != nil { return r } return t } func (t *Trinary) GoString() string { return t.goString(0, "") } func (t *Trinary) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTrinary:\n%s\n%s\n%s\n%s", indent, "", field, t.Op.goString(indent+2, "Op: "), t.First.goString(indent+2, "First: "), t.Second.goString(indent+2, "Second: "), t.Third.goString(indent+2, "Third: ")) } // Fold is a C++17 fold-expression. Arg2 is nil for a unary operator. type Fold struct { Left bool Op AST Arg1 AST Arg2 AST } func (f *Fold) print(ps *printState) { op, _ := f.Op.(*Operator) printOp := func() { if op != nil { if ps.llvmStyle { ps.writeByte(' ') } ps.writeString(op.Name) if ps.llvmStyle { ps.writeByte(' ') } } else { ps.print(f.Op) } } foldParenthesize := func(a AST) { if ps.llvmStyle { prec := precDefault if p, ok := a.(hasPrec); ok { prec = p.prec() } needsParen := false if prec > precCast { needsParen = true } if needsParen { ps.startScope('(') } ps.print(a) if needsParen { ps.endScope(')') } } else { parenthesize(ps, a) } } if f.Arg2 == nil { if f.Left { ps.startScope('(') ps.writeString("...") printOp() foldParenthesize(f.Arg1) ps.endScope(')') } else { ps.startScope('(') foldParenthesize(f.Arg1) printOp() ps.writeString("...") ps.endScope(')') } } else { ps.startScope('(') foldParenthesize(f.Arg1) printOp() ps.writeString("...") printOp() foldParenthesize(f.Arg2) ps.endScope(')') } } func (f *Fold) Traverse(fn func(AST) bool) { if fn(f) { f.Op.Traverse(fn) f.Arg1.Traverse(fn) if f.Arg2 != nil { f.Arg2.Traverse(fn) } } } func (f *Fold) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(f) { return nil } op := f.Op.Copy(fn, skip) arg1 := f.Arg1.Copy(fn, skip) var arg2 AST if f.Arg2 != nil { arg2 = f.Arg2.Copy(fn, skip) } if op == nil && arg1 == nil && arg2 == nil { return fn(f) } if op == nil { op = f.Op } if arg1 == nil { arg1 = f.Arg1 } if arg2 == nil { arg2 = f.Arg2 } f = &Fold{Left: f.Left, Op: op, Arg1: arg1, Arg2: arg2} if r := fn(f); r != nil { return r } return f } func (f *Fold) GoString() string { return f.goString(0, "") } func (f *Fold) goString(indent int, field string) string { if f.Arg2 == nil { return fmt.Sprintf("%*s%sFold: Left: %t\n%s\n%s", indent, "", field, f.Left, f.Op.goString(indent+2, "Op: "), f.Arg1.goString(indent+2, "Arg1: ")) } else { return fmt.Sprintf("%*s%sFold: Left: %t\n%s\n%s\n%s", indent, "", field, f.Left, f.Op.goString(indent+2, "Op: "), f.Arg1.goString(indent+2, "Arg1: "), f.Arg2.goString(indent+2, "Arg2: ")) } } // Subobject is a a reference to an offset in an expression. This is // used for C++20 manglings of class types used as the type of // non-type template arguments. // // See https://github.com/itanium-cxx-abi/cxx-abi/issues/47. type Subobject struct { Type AST SubExpr AST Offset int Selectors []int PastEnd bool } func (so *Subobject) print(ps *printState) { ps.print(so.SubExpr) ps.writeString(".<") ps.print(so.Type) ps.writeString(fmt.Sprintf(" at offset %d>", so.Offset)) } func (so *Subobject) Traverse(fn func(AST) bool) { if fn(so) { so.Type.Traverse(fn) so.SubExpr.Traverse(fn) } } func (so *Subobject) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(so) { return nil } typ := so.Type.Copy(fn, skip) subExpr := so.SubExpr.Copy(fn, skip) if typ == nil && subExpr == nil { return nil } if typ == nil { typ = so.Type } if subExpr == nil { subExpr = so.SubExpr } so = &Subobject{ Type: typ, SubExpr: subExpr, Offset: so.Offset, Selectors: so.Selectors, PastEnd: so.PastEnd, } if r := fn(so); r != nil { return r } return so } func (so *Subobject) GoString() string { return so.goString(0, "") } func (so *Subobject) goString(indent int, field string) string { var selectors string for _, s := range so.Selectors { selectors += fmt.Sprintf(" %d", s) } return fmt.Sprintf("%*s%sSubobject:\n%s\n%s\n%*sOffset: %d\n%*sSelectors:%s\n%*sPastEnd: %t", indent, "", field, so.Type.goString(indent+2, "Type: "), so.SubExpr.goString(indent+2, "SubExpr: "), indent+2, "", so.Offset, indent+2, "", selectors, indent+2, "", so.PastEnd) } // PtrMemCast is a conversion of an expression to a pointer-to-member // type. This is used for C++20 manglings of class types used as the // type of non-type template arguments. // // See https://github.com/itanium-cxx-abi/cxx-abi/issues/47. type PtrMemCast struct { Type AST Expr AST Offset int } func (pmc *PtrMemCast) print(ps *printState) { ps.startScope('(') ps.print(pmc.Type) ps.writeString(")(") ps.print(pmc.Expr) ps.endScope(')') } func (pmc *PtrMemCast) Traverse(fn func(AST) bool) { if fn(pmc) { pmc.Type.Traverse(fn) pmc.Expr.Traverse(fn) } } func (pmc *PtrMemCast) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(pmc) { return nil } typ := pmc.Type.Copy(fn, skip) expr := pmc.Expr.Copy(fn, skip) if typ == nil && expr == nil { return nil } if typ == nil { typ = pmc.Type } if expr == nil { expr = pmc.Expr } pmc = &PtrMemCast{ Type: typ, Expr: expr, Offset: pmc.Offset, } if r := fn(pmc); r != nil { return r } return pmc } func (pmc *PtrMemCast) GoString() string { return pmc.goString(0, "") } func (pmc *PtrMemCast) goString(indent int, field string) string { return fmt.Sprintf("%*s%sPtrMemCast:\n%s\n%s\n%*sOffset: %d", indent, "", field, pmc.Type.goString(indent+2, "Type: "), pmc.Expr.goString(indent+2, "Expr: "), indent+2, "", pmc.Offset) } // New is a use of operator new in an expression. type New struct { Op AST Place AST Type AST Init AST } func (n *New) print(ps *printState) { if !ps.llvmStyle { // Op doesn't really matter for printing--we always print "new". ps.writeString("new ") } else { op, _ := n.Op.(*Operator) if op != nil { ps.writeString(op.Name) if n.Place == nil { ps.writeByte(' ') } } else { ps.print(n.Op) } } if n.Place != nil { parenthesize(ps, n.Place) ps.writeByte(' ') } ps.print(n.Type) if n.Init != nil { parenthesize(ps, n.Init) } } func (n *New) Traverse(fn func(AST) bool) { if fn(n) { n.Op.Traverse(fn) if n.Place != nil { n.Place.Traverse(fn) } n.Type.Traverse(fn) if n.Init != nil { n.Init.Traverse(fn) } } } func (n *New) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(n) { return nil } op := n.Op.Copy(fn, skip) var place AST if n.Place != nil { place = n.Place.Copy(fn, skip) } typ := n.Type.Copy(fn, skip) var ini AST if n.Init != nil { ini = n.Init.Copy(fn, skip) } if op == nil && place == nil && typ == nil && ini == nil { return fn(n) } if op == nil { op = n.Op } if place == nil { place = n.Place } if typ == nil { typ = n.Type } if ini == nil { ini = n.Init } n = &New{Op: op, Place: place, Type: typ, Init: ini} if r := fn(n); r != nil { return r } return n } func (n *New) GoString() string { return n.goString(0, "") } func (n *New) goString(indent int, field string) string { var place string if n.Place == nil { place = fmt.Sprintf("%*sPlace: nil", indent, "") } else { place = n.Place.goString(indent+2, "Place: ") } var ini string if n.Init == nil { ini = fmt.Sprintf("%*sInit: nil", indent, "") } else { ini = n.Init.goString(indent+2, "Init: ") } return fmt.Sprintf("%*s%sNew:\n%s\n%s\n%s\n%s", indent, "", field, n.Op.goString(indent+2, "Op: "), place, n.Type.goString(indent+2, "Type: "), ini) } // Literal is a literal in an expression. type Literal struct { Type AST Val string Neg bool } // Suffixes to use for constants of the given integer type. var builtinTypeSuffix = map[string]string{ "int": "", "unsigned int": "u", "long": "l", "unsigned long": "ul", "long long": "ll", "unsigned long long": "ull", } // Builtin float types. var builtinTypeFloat = map[string]bool{ "double": true, "long double": true, "float": true, "__float128": true, "half": true, } func (l *Literal) print(ps *printState) { isFloat := false if b, ok := l.Type.(*BuiltinType); ok { if suffix, ok := builtinTypeSuffix[b.Name]; ok { if l.Neg { ps.writeByte('-') } ps.writeString(l.Val) ps.writeString(suffix) return } else if b.Name == "bool" && !l.Neg { switch l.Val { case "0": ps.writeString("false") return case "1": ps.writeString("true") return } } else if b.Name == "decltype(nullptr)" && (l.Val == "" || l.Val == "0") { if ps.llvmStyle { ps.writeString("nullptr") } else { ps.print(l.Type) } return } else { isFloat = builtinTypeFloat[b.Name] } } ps.startScope('(') ps.print(l.Type) ps.endScope(')') if isFloat { ps.writeByte('[') } if l.Neg { ps.writeByte('-') } ps.writeString(l.Val) if isFloat { ps.writeByte(']') } } func (l *Literal) Traverse(fn func(AST) bool) { if fn(l) { l.Type.Traverse(fn) } } func (l *Literal) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(l) { return nil } typ := l.Type.Copy(fn, skip) if typ == nil { return fn(l) } l = &Literal{Type: typ, Val: l.Val, Neg: l.Neg} if r := fn(l); r != nil { return r } return l } func (l *Literal) GoString() string { return l.goString(0, "") } func (l *Literal) goString(indent int, field string) string { var neg string if l.Neg { neg = " Neg: true" } return fmt.Sprintf("%*s%sLiteral:%s\n%s\n%*sVal: %s", indent, "", field, neg, l.Type.goString(indent+2, "Type: "), indent+2, "", l.Val) } func (l *Literal) prec() precedence { return precPrimary } // StringLiteral is a string literal. type StringLiteral struct { Type AST } func (sl *StringLiteral) print(ps *printState) { ps.writeString(`"<`) sl.Type.print(ps) ps.writeString(`>"`) } func (sl *StringLiteral) Traverse(fn func(AST) bool) { if fn(sl) { sl.Type.Traverse(fn) } } func (sl *StringLiteral) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(sl) { return nil } typ := sl.Type.Copy(fn, skip) if typ == nil { return fn(sl) } sl = &StringLiteral{Type: typ} if r := fn(sl); r != nil { return r } return sl } func (sl *StringLiteral) GoString() string { return sl.goString(0, "") } func (sl *StringLiteral) goString(indent int, field string) string { return fmt.Sprintf("%*s%sStringLiteral:\n%s", indent, "", field, sl.Type.goString(indent+2, "")) } // LambdaExpr is a literal that is a lambda expression. type LambdaExpr struct { Type AST } func (le *LambdaExpr) print(ps *printState) { ps.writeString("[]") if cl, ok := le.Type.(*Closure); ok { cl.printTypes(ps) } ps.writeString("{...}") } func (le *LambdaExpr) Traverse(fn func(AST) bool) { if fn(le) { le.Type.Traverse(fn) } } func (le *LambdaExpr) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(le) { return nil } typ := le.Type.Copy(fn, skip) if typ == nil { return fn(le) } le = &LambdaExpr{Type: typ} if r := fn(le); r != nil { return r } return le } func (le *LambdaExpr) GoString() string { return le.goString(0, "") } func (le *LambdaExpr) goString(indent int, field string) string { return fmt.Sprintf("%*s%sLambdaExpr:\n%s", indent, "", field, le.Type.goString(indent+2, "")) } // ExprList is a list of expressions, typically arguments to a // function call in an expression. type ExprList struct { Exprs []AST } func (el *ExprList) print(ps *printState) { ps.printList(el.Exprs, nil) } func (el *ExprList) Traverse(fn func(AST) bool) { if fn(el) { for _, e := range el.Exprs { e.Traverse(fn) } } } func (el *ExprList) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(el) { return nil } exprs := make([]AST, len(el.Exprs)) changed := false for i, e := range el.Exprs { ec := e.Copy(fn, skip) if ec == nil { exprs[i] = e } else { exprs[i] = ec changed = true } } if !changed { return fn(el) } el = &ExprList{Exprs: exprs} if r := fn(el); r != nil { return r } return el } func (el *ExprList) GoString() string { return el.goString(0, "") } func (el *ExprList) goString(indent int, field string) string { if len(el.Exprs) == 0 { return fmt.Sprintf("%*s%sExprList: nil", indent, "", field) } s := fmt.Sprintf("%*s%sExprList:", indent, "", field) for i, e := range el.Exprs { s += "\n" s += e.goString(indent+2, fmt.Sprintf("%d: ", i)) } return s } func (el *ExprList) prec() precedence { return precComma } // InitializerList is an initializer list: an optional type with a // list of expressions. type InitializerList struct { Type AST Exprs AST } func (il *InitializerList) print(ps *printState) { if il.Type != nil { ps.print(il.Type) } ps.writeByte('{') ps.print(il.Exprs) ps.writeByte('}') } func (il *InitializerList) Traverse(fn func(AST) bool) { if fn(il) { if il.Type != nil { il.Type.Traverse(fn) } il.Exprs.Traverse(fn) } } func (il *InitializerList) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(il) { return nil } var typ AST if il.Type != nil { typ = il.Type.Copy(fn, skip) } exprs := il.Exprs.Copy(fn, skip) if typ == nil && exprs == nil { return fn(il) } if typ == nil { typ = il.Type } if exprs == nil { exprs = il.Exprs } il = &InitializerList{Type: typ, Exprs: exprs} if r := fn(il); r != nil { return r } return il } func (il *InitializerList) GoString() string { return il.goString(0, "") } func (il *InitializerList) goString(indent int, field string) string { var t string if il.Type == nil { t = fmt.Sprintf("%*sType: nil", indent+2, "") } else { t = il.Type.goString(indent+2, "Type: ") } return fmt.Sprintf("%*s%sInitializerList:\n%s\n%s", indent, "", field, t, il.Exprs.goString(indent+2, "Exprs: ")) } // DefaultArg holds a default argument for a local name. type DefaultArg struct { Num int Arg AST } func (da *DefaultArg) print(ps *printState) { if !ps.llvmStyle { fmt.Fprintf(&ps.buf, "{default arg#%d}::", da.Num+1) } ps.print(da.Arg) } func (da *DefaultArg) Traverse(fn func(AST) bool) { if fn(da) { da.Arg.Traverse(fn) } } func (da *DefaultArg) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(da) { return nil } arg := da.Arg.Copy(fn, skip) if arg == nil { return fn(da) } da = &DefaultArg{Num: da.Num, Arg: arg} if r := fn(da); r != nil { return r } return da } func (da *DefaultArg) GoString() string { return da.goString(0, "") } func (da *DefaultArg) goString(indent int, field string) string { return fmt.Sprintf("%*s%sDefaultArg: Num: %d\n%s", indent, "", field, da.Num, da.Arg.goString(indent+2, "Arg: ")) } // Closure is a closure, or lambda expression. type Closure struct { TemplateArgs []AST TemplateArgsConstraint AST Types []AST Num int CallConstraint AST } func (cl *Closure) print(ps *printState) { if ps.llvmStyle { if cl.Num == 0 { ps.writeString("'lambda'") } else { ps.writeString(fmt.Sprintf("'lambda%d'", cl.Num-1)) } } else { ps.writeString("{lambda") } cl.printTypes(ps) if !ps.llvmStyle { ps.writeString(fmt.Sprintf("#%d}", cl.Num+1)) } } func (cl *Closure) printTypes(ps *printState) { if len(cl.TemplateArgs) > 0 { scopes := ps.scopes ps.scopes = 0 ps.writeString("<") ps.printList(cl.TemplateArgs, nil) ps.writeString(">") ps.scopes = scopes } if cl.TemplateArgsConstraint != nil { ps.writeString(" requires ") ps.print(cl.TemplateArgsConstraint) ps.writeByte(' ') } ps.startScope('(') ps.printList(cl.Types, nil) ps.endScope(')') if cl.CallConstraint != nil { ps.writeString(" requires ") ps.print(cl.CallConstraint) } } func (cl *Closure) Traverse(fn func(AST) bool) { if fn(cl) { for _, a := range cl.TemplateArgs { a.Traverse(fn) } if cl.TemplateArgsConstraint != nil { cl.TemplateArgsConstraint.Traverse(fn) } for _, t := range cl.Types { t.Traverse(fn) } if cl.CallConstraint != nil { cl.CallConstraint.Traverse(fn) } } } func (cl *Closure) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(cl) { return nil } changed := false args := make([]AST, len(cl.TemplateArgs)) for i, a := range cl.TemplateArgs { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac changed = true } } var templateArgsConstraint AST if cl.TemplateArgsConstraint != nil { templateArgsConstraint = cl.TemplateArgsConstraint.Copy(fn, skip) if templateArgsConstraint == nil { templateArgsConstraint = cl.TemplateArgsConstraint } else { changed = true } } types := make([]AST, len(cl.Types)) for i, t := range cl.Types { tc := t.Copy(fn, skip) if tc == nil { types[i] = t } else { types[i] = tc changed = true } } var callConstraint AST if cl.CallConstraint != nil { callConstraint = cl.CallConstraint.Copy(fn, skip) if callConstraint == nil { callConstraint = cl.CallConstraint } else { changed = true } } if !changed { return fn(cl) } cl = &Closure{ TemplateArgs: args, TemplateArgsConstraint: templateArgsConstraint, Types: types, Num: cl.Num, CallConstraint: callConstraint, } if r := fn(cl); r != nil { return r } return cl } func (cl *Closure) GoString() string { return cl.goString(0, "") } func (cl *Closure) goString(indent int, field string) string { var args strings.Builder if len(cl.TemplateArgs) == 0 { fmt.Fprintf(&args, "%*sTemplateArgs: nil", indent+2, "") } else { fmt.Fprintf(&args, "%*sTemplateArgs:", indent+2, "") for i, a := range cl.TemplateArgs { args.WriteByte('\n') args.WriteString(a.goString(indent+4, fmt.Sprintf("%d: ", i))) } } var templateArgsConstraint string if cl.TemplateArgsConstraint != nil { templateArgsConstraint = "\n" + cl.TemplateArgsConstraint.goString(indent+2, "TemplateArgsConstraint: ") } var types strings.Builder if len(cl.Types) == 0 { fmt.Fprintf(&types, "%*sTypes: nil", indent+2, "") } else { fmt.Fprintf(&types, "%*sTypes:", indent+2, "") for i, t := range cl.Types { types.WriteByte('\n') types.WriteString(t.goString(indent+4, fmt.Sprintf("%d: ", i))) } } var callConstraint string if cl.CallConstraint != nil { callConstraint = "\n" + cl.CallConstraint.goString(indent+2, "CallConstraint: ") } return fmt.Sprintf("%*s%sClosure: Num: %d\n%s\n%s%s%s", indent, "", field, cl.Num, args.String(), templateArgsConstraint, types.String(), callConstraint) } // StructuredBindings is a structured binding declaration. type StructuredBindings struct { Bindings []AST } func (sb *StructuredBindings) print(ps *printState) { ps.writeString("[") ps.printList(sb.Bindings, nil) ps.writeString("]") } func (sb *StructuredBindings) Traverse(fn func(AST) bool) { if fn(sb) { for _, b := range sb.Bindings { b.Traverse(fn) } } } func (sb *StructuredBindings) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(sb) { return nil } changed := false bindings := make([]AST, len(sb.Bindings)) for i, b := range sb.Bindings { bc := b.Copy(fn, skip) if bc == nil { bindings[i] = b } else { bindings[i] = bc changed = true } } if !changed { return fn(sb) } sb = &StructuredBindings{Bindings: bindings} if r := fn(sb); r != nil { return r } return sb } func (sb *StructuredBindings) GoString() string { return sb.goString(0, "") } func (sb *StructuredBindings) goString(indent int, field string) string { var strb strings.Builder fmt.Fprintf(&strb, "%*s%sStructuredBinding:", indent, "", field) for _, b := range sb.Bindings { strb.WriteByte('\n') strb.WriteString(b.goString(indent+2, "")) } return strb.String() } // UnnamedType is an unnamed type, that just has an index. type UnnamedType struct { Num int } func (ut *UnnamedType) print(ps *printState) { if ps.llvmStyle { if ut.Num == 0 { ps.writeString("'unnamed'") } else { ps.writeString(fmt.Sprintf("'unnamed%d'", ut.Num-1)) } } else { ps.writeString(fmt.Sprintf("{unnamed type#%d}", ut.Num+1)) } } func (ut *UnnamedType) Traverse(fn func(AST) bool) { fn(ut) } func (ut *UnnamedType) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ut) { return nil } return fn(ut) } func (ut *UnnamedType) GoString() string { return ut.goString(0, "") } func (ut *UnnamedType) goString(indent int, field string) string { return fmt.Sprintf("%*s%sUnnamedType: Num: %d", indent, "", field, ut.Num) } // Clone is a clone of a function, with a distinguishing suffix. type Clone struct { Base AST Suffix string } func (c *Clone) print(ps *printState) { ps.print(c.Base) if ps.llvmStyle { ps.writeByte(' ') ps.startScope('(') ps.writeString(c.Suffix) ps.endScope(')') } else { ps.writeString(fmt.Sprintf(" [clone %s]", c.Suffix)) } } func (c *Clone) Traverse(fn func(AST) bool) { if fn(c) { c.Base.Traverse(fn) } } func (c *Clone) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(c) { return nil } base := c.Base.Copy(fn, skip) if base == nil { return fn(c) } c = &Clone{Base: base, Suffix: c.Suffix} if r := fn(c); r != nil { return r } return c } func (c *Clone) GoString() string { return c.goString(0, "") } func (c *Clone) goString(indent int, field string) string { return fmt.Sprintf("%*s%sClone: Suffix: %s\n%s", indent, "", field, c.Suffix, c.Base.goString(indent+2, "Base: ")) } // Special is a special symbol, printed as a prefix plus another // value. type Special struct { Prefix string Val AST } func (s *Special) print(ps *printState) { prefix := s.Prefix if ps.llvmStyle { switch prefix { case "TLS wrapper function for ": prefix = "thread-local wrapper routine for " case "TLS init function for ": prefix = "thread-local initialization routine for " } } ps.writeString(prefix) ps.print(s.Val) } func (s *Special) Traverse(fn func(AST) bool) { if fn(s) { s.Val.Traverse(fn) } } func (s *Special) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(s) { return nil } val := s.Val.Copy(fn, skip) if val == nil { return fn(s) } s = &Special{Prefix: s.Prefix, Val: val} if r := fn(s); r != nil { return r } return s } func (s *Special) GoString() string { return s.goString(0, "") } func (s *Special) goString(indent int, field string) string { return fmt.Sprintf("%*s%sSpecial: Prefix: %s\n%s", indent, "", field, s.Prefix, s.Val.goString(indent+2, "Val: ")) } // Special2 is like special, but uses two values. type Special2 struct { Prefix string Val1 AST Middle string Val2 AST } func (s *Special2) print(ps *printState) { ps.writeString(s.Prefix) ps.print(s.Val1) ps.writeString(s.Middle) ps.print(s.Val2) } func (s *Special2) Traverse(fn func(AST) bool) { if fn(s) { s.Val1.Traverse(fn) s.Val2.Traverse(fn) } } func (s *Special2) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(s) { return nil } val1 := s.Val1.Copy(fn, skip) val2 := s.Val2.Copy(fn, skip) if val1 == nil && val2 == nil { return fn(s) } if val1 == nil { val1 = s.Val1 } if val2 == nil { val2 = s.Val2 } s = &Special2{Prefix: s.Prefix, Val1: val1, Middle: s.Middle, Val2: val2} if r := fn(s); r != nil { return r } return s } func (s *Special2) GoString() string { return s.goString(0, "") } func (s *Special2) goString(indent int, field string) string { return fmt.Sprintf("%*s%sSpecial2: Prefix: %s\n%s\n%*sMiddle: %s\n%s", indent, "", field, s.Prefix, s.Val1.goString(indent+2, "Val1: "), indent+2, "", s.Middle, s.Val2.goString(indent+2, "Val2: ")) } // EnableIf is used by clang for an enable_if attribute. type EnableIf struct { Type AST Args []AST } func (ei *EnableIf) print(ps *printState) { ps.print(ei.Type) ps.writeString(" [enable_if:") ps.printList(ei.Args, nil) ps.writeString("]") } func (ei *EnableIf) Traverse(fn func(AST) bool) { if fn(ei) { ei.Type.Traverse(fn) for _, a := range ei.Args { a.Traverse(fn) } } } func (ei *EnableIf) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(ei) { return nil } typ := ei.Type.Copy(fn, skip) argsChanged := false args := make([]AST, len(ei.Args)) for i, a := range ei.Args { ac := a.Copy(fn, skip) if ac == nil { args[i] = a } else { args[i] = ac argsChanged = true } } if typ == nil && !argsChanged { return fn(ei) } if typ == nil { typ = ei.Type } ei = &EnableIf{Type: typ, Args: args} if r := fn(ei); r != nil { return r } return ei } func (ei *EnableIf) GoString() string { return ei.goString(0, "") } func (ei *EnableIf) goString(indent int, field string) string { var args string if len(ei.Args) == 0 { args = fmt.Sprintf("%*sArgs: nil", indent+2, "") } else { args = fmt.Sprintf("%*sArgs:", indent+2, "") for i, a := range ei.Args { args += "\n" args += a.goString(indent+4, fmt.Sprintf("%d: ", i)) } } return fmt.Sprintf("%*s%sEnableIf:\n%s\n%s", indent, "", field, ei.Type.goString(indent+2, "Type: "), args) } // ModuleName is a C++20 module. type ModuleName struct { Parent AST Name AST IsPartition bool } func (mn *ModuleName) print(ps *printState) { if mn.Parent != nil { ps.print(mn.Parent) } if mn.IsPartition { ps.writeByte(':') } else if mn.Parent != nil { ps.writeByte('.') } ps.print(mn.Name) } func (mn *ModuleName) Traverse(fn func(AST) bool) { if fn(mn) { mn.Parent.Traverse(fn) mn.Name.Traverse(fn) } } func (mn *ModuleName) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(mn) { return nil } var parent AST if mn.Parent != nil { parent = mn.Parent.Copy(fn, skip) } name := mn.Name.Copy(fn, skip) if parent == nil && name == nil { return fn(mn) } if parent == nil { parent = mn.Parent } if name == nil { name = mn.Name } mn = &ModuleName{Parent: parent, Name: name, IsPartition: mn.IsPartition} if r := fn(mn); r != nil { return r } return mn } func (mn *ModuleName) GoString() string { return mn.goString(0, "") } func (mn *ModuleName) goString(indent int, field string) string { var parent string if mn.Parent == nil { parent = fmt.Sprintf("%*sParent: nil", indent+2, "") } else { parent = mn.Parent.goString(indent+2, "Parent: ") } return fmt.Sprintf("%*s%sModuleName: IsPartition: %t\n%s\n%s", indent, "", field, mn.IsPartition, parent, mn.Name.goString(indent+2, "Name: ")) } // ModuleEntity is a name inside a module. type ModuleEntity struct { Module AST Name AST } func (me *ModuleEntity) print(ps *printState) { ps.print(me.Name) ps.writeByte('@') ps.print(me.Module) } func (me *ModuleEntity) Traverse(fn func(AST) bool) { if fn(me) { me.Module.Traverse(fn) me.Name.Traverse(fn) } } func (me *ModuleEntity) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(me) { return nil } module := me.Module.Copy(fn, skip) name := me.Name.Copy(fn, skip) if module == nil && name == nil { return fn(me) } if module == nil { module = me.Module } if name == nil { name = me.Name } me = &ModuleEntity{Module: module, Name: name} if r := fn(me); r != nil { return r } return me } func (me *ModuleEntity) GoString() string { return me.goString(0, "") } func (me *ModuleEntity) goString(indent int, field string) string { return fmt.Sprintf("%*s%sModuleEntity:\n%s\n%s", indent, "", field, me.Module.goString(indent+2, "Module: "), me.Name.goString(indent+2, "Name: ")) } // Friend is a member like friend name. type Friend struct { Name AST } func (f *Friend) print(ps *printState) { ps.writeString("friend ") ps.print(f.Name) } func (f *Friend) Traverse(fn func(AST) bool) { if fn(f) { f.Name.Traverse(fn) } } func (f *Friend) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(f) { return nil } name := f.Name.Copy(fn, skip) if name == nil { return fn(f) } f = &Friend{Name: name} if r := fn(f); r != nil { return r } return f } func (f *Friend) GoString() string { return f.goString(0, "") } func (f *Friend) goString(indent int, field string) string { return fmt.Sprintf("%*s%sFriend:\n%s", indent, "", field, f.Name.goString(indent+2, "Name: ")) } // Constraint represents an AST with a constraint. type Constraint struct { Name AST Requires AST } func (c *Constraint) print(ps *printState) { ps.print(c.Name) ps.writeString(" requires ") ps.print(c.Requires) } func (c *Constraint) Traverse(fn func(AST) bool) { if fn(c) { c.Name.Traverse(fn) c.Requires.Traverse(fn) } } func (c *Constraint) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(c) { return nil } name := c.Name.Copy(fn, skip) requires := c.Requires.Copy(fn, skip) if name == nil && requires == nil { return fn(c) } if name == nil { name = c.Name } if requires == nil { requires = c.Requires } c = &Constraint{Name: name, Requires: requires} if r := fn(c); r != nil { return r } return c } func (c *Constraint) GoString() string { return c.goString(0, "") } func (c *Constraint) goString(indent int, field string) string { return fmt.Sprintf("%*s%sConstraint:\n%s\n%s", indent, "", field, c.Name.goString(indent+2, "Name: "), c.Requires.goString(indent+2, "Requires: ")) } // RequiresExpr is a C++20 requires expression. type RequiresExpr struct { Params []AST Requirements []AST } func (re *RequiresExpr) print(ps *printState) { ps.writeString("requires") if len(re.Params) > 0 { ps.writeByte(' ') ps.startScope('(') ps.printList(re.Params, nil) ps.endScope(')') } ps.writeByte(' ') ps.startScope('{') for _, req := range re.Requirements { ps.print(req) } ps.writeByte(' ') ps.endScope('}') } func (re *RequiresExpr) Traverse(fn func(AST) bool) { if fn(re) { for _, p := range re.Params { p.Traverse(fn) } for _, r := range re.Requirements { r.Traverse(fn) } } } func (re *RequiresExpr) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(re) { return nil } changed := false var params []AST if len(re.Params) > 0 { params = make([]AST, len(re.Params)) for i, p := range re.Params { pc := p.Copy(fn, skip) if pc == nil { params[i] = p } else { params[i] = pc changed = true } } } requirements := make([]AST, len(re.Requirements)) for i, r := range re.Requirements { rc := r.Copy(fn, skip) if rc == nil { requirements[i] = r } else { requirements[i] = rc changed = true } } if !changed { return fn(re) } re = &RequiresExpr{Params: params, Requirements: requirements} if r := fn(re); r != nil { return r } return re } func (re *RequiresExpr) GoString() string { return re.goString(0, "") } func (re *RequiresExpr) goString(indent int, field string) string { var params strings.Builder if len(re.Params) == 0 { fmt.Fprintf(¶ms, "%*sParams: nil", indent+2, "") } else { fmt.Fprintf(¶ms, "%*sParams:", indent+2, "") for i, p := range re.Params { params.WriteByte('\n') params.WriteString(p.goString(indent+4, fmt.Sprintf("%d: ", i))) } } var requirements strings.Builder fmt.Fprintf(&requirements, "%*sRequirements:", indent+2, "") for i, r := range re.Requirements { requirements.WriteByte('\n') requirements.WriteString(r.goString(indent+4, fmt.Sprintf("%d: ", i))) } return fmt.Sprintf("%*s%sRequirements:\n%s\n%s", indent, "", field, params.String(), requirements.String()) } // ExprRequirement is a simple requirement in a requires expression. // This is an arbitrary expression. type ExprRequirement struct { Expr AST Noexcept bool TypeReq AST } func (er *ExprRequirement) print(ps *printState) { ps.writeByte(' ') if er.Noexcept || er.TypeReq != nil { ps.startScope('{') } ps.print(er.Expr) if er.Noexcept || er.TypeReq != nil { ps.endScope('}') } if er.Noexcept { ps.writeString(" noexcept") } if er.TypeReq != nil { ps.writeString(" -> ") ps.print(er.TypeReq) } ps.writeByte(';') } func (er *ExprRequirement) Traverse(fn func(AST) bool) { if fn(er) { er.Expr.Traverse(fn) if er.TypeReq != nil { er.TypeReq.Traverse(fn) } } } func (er *ExprRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(er) { return nil } expr := er.Expr.Copy(fn, skip) var typeReq AST if er.TypeReq != nil { typeReq = er.TypeReq.Copy(fn, skip) } if expr == nil && typeReq == nil { return fn(er) } if expr == nil { expr = er.Expr } if typeReq == nil { typeReq = er.TypeReq } er = &ExprRequirement{Expr: expr, TypeReq: typeReq} if r := fn(er); r != nil { return r } return er } func (er *ExprRequirement) GoString() string { return er.goString(0, "") } func (er *ExprRequirement) goString(indent int, field string) string { var typeReq string if er.TypeReq != nil { typeReq = "\n" + er.TypeReq.goString(indent+2, "TypeReq: ") } return fmt.Sprintf("%*s%sExprRequirement: Noexcept: %t\n%s%s", indent, "", field, er.Noexcept, er.Expr.goString(indent+2, "Expr: "), typeReq) } // TypeRequirement is a type requirement in a requires expression. type TypeRequirement struct { Type AST } func (tr *TypeRequirement) print(ps *printState) { ps.writeString(" typename ") ps.print(tr.Type) ps.writeByte(';') } func (tr *TypeRequirement) Traverse(fn func(AST) bool) { if fn(tr) { tr.Type.Traverse(fn) } } func (tr *TypeRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(tr) { return nil } typ := tr.Type.Copy(fn, skip) if typ == nil { return fn(tr) } tr = &TypeRequirement{Type: typ} if r := fn(tr); r != nil { return r } return tr } func (tr *TypeRequirement) GoString() string { return tr.goString(0, "") } func (tr *TypeRequirement) goString(indent int, field string) string { return fmt.Sprintf("%*s%sTypeRequirement:\n%s", indent, "", field, tr.Type.goString(indent+2, "")) } // NestedRequirement is a nested requirement in a requires expression. type NestedRequirement struct { Constraint AST } func (nr *NestedRequirement) print(ps *printState) { ps.writeString(" requires ") ps.print(nr.Constraint) ps.writeByte(';') } func (nr *NestedRequirement) Traverse(fn func(AST) bool) { if fn(nr) { nr.Constraint.Traverse(fn) } } func (nr *NestedRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(nr) { return nil } constraint := nr.Constraint.Copy(fn, skip) if constraint == nil { return fn(nr) } nr = &NestedRequirement{Constraint: constraint} if r := fn(nr); r != nil { return r } return nr } func (nr *NestedRequirement) GoString() string { return nr.goString(0, "") } func (nr *NestedRequirement) goString(indent int, field string) string { return fmt.Sprintf("%*s%sNestedRequirement:\n%s", indent, "", field, nr.Constraint.goString(indent+2, "")) } // ExplicitObjectParameter represents a C++23 explicit object parameter. type ExplicitObjectParameter struct { Base AST } func (eop *ExplicitObjectParameter) print(ps *printState) { ps.writeString("this ") ps.print(eop.Base) } func (eop *ExplicitObjectParameter) Traverse(fn func(AST) bool) { if fn(eop) { eop.Base.Traverse(fn) } } func (eop *ExplicitObjectParameter) Copy(fn func(AST) AST, skip func(AST) bool) AST { if skip(eop) { return nil } base := eop.Base.Copy(fn, skip) if base == nil { return fn(eop) } eop = &ExplicitObjectParameter{Base: base} if r := fn(eop); r != nil { return r } return eop } func (eop *ExplicitObjectParameter) GoString() string { return eop.goString(0, "") } func (eop *ExplicitObjectParameter) goString(indent int, field string) string { return fmt.Sprintf("%*s%sExplicitObjectParameter:\n%s", indent, "", field, eop.Base.goString(indent+2, "")) } // Print the inner types. func (ps *printState) printInner(prefixOnly bool) []AST { var save []AST var psave *[]AST if prefixOnly { psave = &save } for len(ps.inner) > 0 { ps.printOneInner(psave) } return save } // innerPrinter is an interface for types that can print themselves as // inner types. type innerPrinter interface { printInner(*printState) } // Print the most recent inner type. If save is not nil, only print // prefixes. func (ps *printState) printOneInner(save *[]AST) { if len(ps.inner) == 0 { panic("printOneInner called with no inner types") } ln := len(ps.inner) a := ps.inner[ln-1] ps.inner = ps.inner[:ln-1] if save != nil { if _, ok := a.(*MethodWithQualifiers); ok { *save = append(*save, a) return } } if ip, ok := a.(innerPrinter); ok { ip.printInner(ps) } else { ps.print(a) } } // isEmpty returns whether printing a will not print anything. func (ps *printState) isEmpty(a AST) bool { switch a := a.(type) { case *ArgumentPack: for _, a := range a.Args { if !ps.isEmpty(a) { return false } } return true case *ExprList: return len(a.Exprs) == 0 case *PackExpansion: return a.Pack != nil && ps.isEmpty(a.Base) default: return false } }
// 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++ and Rust symbol names. // This package recognizes names that were mangled according to the C++ ABI // defined at http://codesourcery.com/cxx-abi/ and the Rust ABI // defined at // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html // // Most programs will want to call Filter or ToString. 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++ or Rust mangled name") // Option is the type of demangler options. type Option int const ( // The NoParams option disables demangling of function parameters. // It only omits the parameters of the function name being demangled, // not the parameter types of other functions that may be mentioned. // Using the option will speed up the demangler and cause it to // use less memory. NoParams Option = iota // The NoTemplateParams option disables demangling of template parameters. // This applies to both C++ and Rust. NoTemplateParams // The NoEnclosingParams option disables demangling of the function // parameter types of the enclosing function when demangling a // local name defined within a function. NoEnclosingParams // The NoClones option disables inclusion of clone suffixes. // NoParams implies NoClones. NoClones // The NoRust option disables demangling of old-style Rust // mangled names, which can be confused with C++ style mangled // names. New style Rust mangled names are still recognized. NoRust // The Verbose option turns on more verbose demangling. Verbose // LLVMStyle tries to translate an AST to a string in the // style of the LLVM demangler. This does not affect // the parsing of the AST, only the conversion of the AST // to a string. LLVMStyle ) // maxLengthShift is how we shift the MaxLength value. const maxLengthShift = 16 // maxLengthMask is a mask for the maxLength value. const maxLengthMask = 0x1f << maxLengthShift // MaxLength returns an Option that limits the maximum length of a // demangled string. The maximum length is expressed as a power of 2, // so a value of 1 limits the returned string to 2 characters, and // a value of 16 limits the returned string to 65,536 characters. // The value must be between 1 and 30. func MaxLength(pow int) Option { if pow <= 0 || pow > 30 { panic("demangle: invalid MaxLength value") } return Option(pow << maxLengthShift) } // isMaxLength reports whether an Option holds a maximum length. func isMaxLength(opt Option) bool { return opt&maxLengthMask != 0 } // maxLength returns the maximum length stored in an Option. func maxLength(opt Option) int { return 1 << ((opt & maxLengthMask) >> maxLengthShift) } // Filter demangles a C++ or Rust symbol name, // returning the human-readable C++ or Rust 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++ or Rust symbol name, // returning a human-readable C++ or Rust name or an error. // If the name does not appear to be a C++ or Rust symbol name at all, // the error will be ErrNotMangledName. func ToString(name string, options ...Option) (string, error) { if strings.HasPrefix(name, "_R") { return rustToString(name, options) } // Check for an old-style Rust mangled name. // It starts with _ZN and ends with "17h" followed by 16 hex digits // followed by "E" followed by an optional suffix starting with "." // (which we ignore). if strings.HasPrefix(name, "_ZN") { rname := name if pos := strings.LastIndex(rname, "E."); pos > 0 { rname = rname[:pos+1] } if strings.HasSuffix(rname, "E") && len(rname) > 23 && rname[len(rname)-20:len(rname)-17] == "17h" { noRust := false for _, o := range options { if o == NoRust { noRust = true break } } if !noRust { s, ok := oldRustToString(rname, options) if ok { return s, nil } } } } 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. // This function does not currently support Rust symbol names. func ToAST(name string, options ...Option) (AST, error) { if strings.HasPrefix(name, "_Z") { a, err := doDemangle(name[2:], options...) return a, adjustErr(err, 2) } if strings.HasPrefix(name, "___Z") { // clang extensions block := strings.LastIndex(name, "_block_invoke") if block == -1 { return nil, ErrNotMangledName } a, err := doDemangle(name[4:block], options...) if err != nil { return a, adjustErr(err, 4) } name = strings.TrimPrefix(name[block:], "_block_invoke") if len(name) > 0 && name[0] == '_' { name = name[1:] } for len(name) > 0 && isDigit(name[0]) { name = name[1:] } if len(name) > 0 && name[0] != '.' { return nil, errors.New("unparsed characters at end of mangled name") } a = &Special{Prefix: "invocation function for block in ", Val: a} return a, nil } 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: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 { case o == NoParams: params = false clones = false case o == NoClones: clones = false case o == Verbose: verbose = true case o == NoTemplateParams || o == NoEnclosingParams || o == LLVMStyle || isMaxLength(o): // These are valid options but only affect // printing of the AST. case o == NoRust: // Unimportant here. 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 // The number of entries in templates when we started parsing // a lambda, plus 1 so that 0 means not parsing a lambda. lambdaTemplateLevel int parsingConstraint bool // whether parsing a constraint expression // Counts of template parameters without template arguments, // for lambdas. typeTemplateParamCount int nonTypeTemplateParamCount int templateTemplateParamCount int } // 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 parses: // // encoding ::= <(function) name> <bare-function-type> // <(data) name> // <special-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, explicitObjectParameter := 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 } mwq, _ := a.(*MethodWithQualifiers) var findTemplate func(AST) *Template findTemplate = func(check AST) *Template { switch check := check.(type) { case *Template: return check case *Qualified: if check.LocalName { return findTemplate(check.Name) } else if _, ok := check.Name.(*Constructor); ok { return findTemplate(check.Name) } case *MethodWithQualifiers: return findTemplate(check.Method) case *Constructor: if check.Base != nil { return findTemplate(check.Base) } } return nil } template := findTemplate(a) var oldLambdaTemplateLevel int if template != nil { st.templates = append(st.templates, template) oldLambdaTemplateLevel = st.lambdaTemplateLevel st.lambdaTemplateLevel = 0 } // Checking for the enable_if attribute here is what the LLVM // demangler does. This is not very general but perhaps it is // sufficient. const enableIfPrefix = "Ua9enable_ifI" var enableIfArgs []AST if strings.HasPrefix(st.str, enableIfPrefix) { st.advance(len(enableIfPrefix) - 1) enableIfArgs = st.templateArgs() } ft := st.bareFunctionType(hasReturnType(a), explicitObjectParameter) var constraint AST if len(st.str) > 0 && st.str[0] == 'Q' { constraint = st.constraintExpr() } if template != nil { st.templates = st.templates[:len(st.templates)-1] st.lambdaTemplateLevel = oldLambdaTemplateLevel } 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.ForLocalName = true } } // 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 } } r := AST(&Typed{Name: a, Type: ft}) if len(enableIfArgs) > 0 { r = &EnableIf{Type: r, Args: enableIfArgs} } if constraint != nil { r = &Constraint{Name: r, Requires: constraint} } return r } // hasReturnType returns whether the mangled form of a will have a // return type. func hasReturnType(a AST) bool { switch a := a.(type) { case *Qualified: if a.LocalName { return hasReturnType(a.Name) } return false 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 } } // taggedName parses: // // <tagged-name> ::= <name> B <source-name> 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 } // name parses: // // <name> ::= <nested-name> // ::= <unscoped-name> // ::= <unscoped-template-name> <template-args> // ::= <local-name> // // <unscoped-name> ::= <unqualified-name> // ::= St <unqualified-name> // // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> // // Besides the name, this returns whether it saw the code indicating // a C++23 explicit object parameter. func (st *state) name() (AST, bool) { if len(st.str) < 1 { st.fail("expected name") } var module AST switch st.str[0] { case 'N': return st.nestedName() case 'Z': return st.localName() case 'U': a, isCast := st.unqualifiedName(nil) if isCast { st.setTemplate(a, nil) } return a, false 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(nil) a = &Qualified{Scope: &Name{Name: "std"}, Name: a, LocalName: false} } else { a = st.substitution(false) if mn, ok := a.(*ModuleName); ok { module = mn break } subst = true } if len(st.str) > 0 && st.str[0] == 'I' { // This can only happen if we saw // <unscoped-template-name> and are about to see // <template-args>. <unscoped-template-name> 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, false } a, isCast := st.unqualifiedName(module) 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, false } // nestedName parses: // // <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E // // Besides the name, this returns whether it saw the code indicating // a C++23 explicit object parameter. func (st *state) nestedName() (AST, bool) { st.checkChar('N') var q AST var r string explicitObjectParameter := false if len(st.str) > 0 && st.str[0] == 'H' { st.advance(1) explicitObjectParameter = true } else { q = st.cvQualifiers() r = st.refQualifier() } a := st.prefix() if q != nil || 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, explicitObjectParameter } // prefix parses: // // <prefix> ::= <prefix> <unqualified-name> // ::= <template-prefix> <template-args> // ::= <template-param> // ::= <decltype> // ::= // ::= <substitution> // // <template-prefix> ::= <prefix> <(template) unqualified-name> // ::= <template-param> // ::= <substitution> // // <decltype> ::= Dt <expression> E // ::= DT <expression> E func (st *state) prefix() AST { var a AST // The last name seen, for a constructor/destructor. var last AST var module 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 } } } var cast *Cast 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' || c == 'F' || c == 'W' || (c == 'D' && len(st.str) > 1 && st.str[1] == 'C') { un, isUnCast := st.unqualifiedName(module) next = un module = nil if isUnCast { if tn, ok := un.(*TaggedName); ok { un = tn.Name } cast = un.(*Cast) } } 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) var base AST if inheriting { base = st.demangleType(false) } next = &Constructor{ Name: getLast(last), Base: base, } if len(st.str) > 0 && st.str[0] == 'B' { next = st.taggedName(next) } 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)} if len(st.str) > 0 && st.str[0] == 'B' { next = st.taggedName(next) } } case 'S': next = st.substitution(true) if mn, ok := next.(*ModuleName); ok { module = mn next = nil } case 'I': if a == nil { st.fail("unexpected template arguments") } var args []AST args = st.templateArgs() tmpl := &Template{Name: a, Args: args} if cast != nil { st.setTemplate(cast, tmpl) st.clearTemplateArgs(args) cast = nil } a = nil next = tmpl case 'T': next = st.templateParam() case 'E': if a == nil { st.fail("expected prefix") } if cast != nil { var toTmpl *Template if castTempl, ok := cast.To.(*Template); ok { toTmpl = castTempl } st.setTemplate(cast, toTmpl) } 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 case 'J': // It appears that in some cases clang // can emit a J for a template arg // without the expected I. I don't // know when this happens, but I've // seen it in some large C++ programs. if a == nil { st.fail("unexpected template arguments") } var args []AST for len(st.str) == 0 || st.str[0] != 'E' { arg := st.templateArg(nil) args = append(args, arg) } st.advance(1) tmpl := &Template{Name: a, Args: args} if cast != nil { st.setTemplate(cast, tmpl) st.clearTemplateArgs(args) cast = nil } a = nil next = tmpl default: st.fail("unrecognized letter in prefix") } } if next == nil { continue } 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) } } } // unqualifiedName parses: // // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> // ::= <source-name> // ::= <local-source-name> // // <local-source-name> ::= L <source-name> <discriminator> func (st *state) unqualifiedName(module AST) (r AST, isCast bool) { if len(st.str) < 1 { st.fail("expected unqualified name") } module = st.moduleName(module) friend := false if len(st.str) > 0 && st.str[0] == 'F' { st.advance(1) friend = true 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 if c == 'D' && len(st.str) > 1 && st.str[1] == 'C' { var bindings []AST st.advance(2) for { binding := st.sourceName() bindings = append(bindings, binding) if len(st.str) > 0 && st.str[0] == 'E' { st.advance(1) break } } a = &StructuredBindings{Bindings: bindings} } 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 'b': st.advance(2) st.compactNumber() a = &Name{Name: "'block-literal'"} 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 module != nil { a = &ModuleEntity{Module: module, Name: a} } if len(st.str) > 0 && st.str[0] == 'B' { a = st.taggedName(a) } if friend { a = &Friend{Name: a} } return a, isCast } // sourceName parses: // // <source-name> ::= <(positive length) number> <identifier> // 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 } // moduleName parses: // // <module-name> ::= <module-subname> // ::= <module-name> <module-subname> // ::= <substitution> # passed in by caller // <module-subname> ::= W <source-name> // ::= W P <source-name> // // The module name is optional. If it is not present, this returns the parent. func (st *state) moduleName(parent AST) AST { ret := parent for len(st.str) > 0 && st.str[0] == 'W' { st.advance(1) isPartition := false if len(st.str) > 0 && st.str[0] == 'P' { st.advance(1) isPartition = true } name := st.sourceName() ret = &ModuleName{ Parent: ret, Name: name, IsPartition: isPartition, } st.subs.add(ret) } return ret } // number parses: // // 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 } // seqID parses: // // <seq-id> ::= <0-9A-Z>+ // // We expect this to be followed by an underscore. func (st *state) seqID(eofOK bool) int { if len(st.str) > 0 && st.str[0] == '_' { st.advance(1) return 0 } id := 0 for { if len(st.str) == 0 { if eofOK { return id + 1 } st.fail("missing end to sequence ID") } // Don't overflow a 32-bit int. if id >= 0x80000000/36-36 { st.fail("sequence ID overflow") } c := st.str[0] if c == '_' { st.advance(1) return id + 1 } 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 sequence ID") } st.advance(1) } } // An operator is the demangled name, and the number of arguments it // takes in an expression. type operator struct { name string args int prec precedence } // The operators map maps the mangled operator names to information // about them. var operators = map[string]operator{ "aN": {"&=", 2, precAssign}, "aS": {"=", 2, precAssign}, "aa": {"&&", 2, precLogicalAnd}, "ad": {"&", 1, precUnary}, "an": {"&", 2, precAnd}, "at": {"alignof ", 1, precUnary}, "aw": {"co_await ", 1, precPrimary}, "az": {"alignof ", 1, precUnary}, "cc": {"const_cast", 2, precPostfix}, "cl": {"()", 2, precPostfix}, // cp is not in the ABI but is used by clang "when the call // would use ADL except for being parenthesized." "cp": {"()", 2, precPostfix}, "cm": {",", 2, precComma}, "co": {"~", 1, precUnary}, "dV": {"/=", 2, precAssign}, "dX": {"[...]=", 3, precAssign}, "da": {"delete[] ", 1, precUnary}, "dc": {"dynamic_cast", 2, precPostfix}, "de": {"*", 1, precUnary}, "di": {"=", 2, precAssign}, "dl": {"delete ", 1, precUnary}, "ds": {".*", 2, precPtrMem}, "dt": {".", 2, precPostfix}, "dv": {"/", 2, precAssign}, "dx": {"]=", 2, precAssign}, "eO": {"^=", 2, precAssign}, "eo": {"^", 2, precXor}, "eq": {"==", 2, precEqual}, "fl": {"...", 2, precPrimary}, "fr": {"...", 2, precPrimary}, "fL": {"...", 3, precPrimary}, "fR": {"...", 3, precPrimary}, "ge": {">=", 2, precRel}, "gs": {"::", 1, precUnary}, "gt": {">", 2, precRel}, "ix": {"[]", 2, precPostfix}, "lS": {"<<=", 2, precAssign}, "le": {"<=", 2, precRel}, "li": {`operator"" `, 1, precUnary}, "ls": {"<<", 2, precShift}, "lt": {"<", 2, precRel}, "mI": {"-=", 2, precAssign}, "mL": {"*=", 2, precAssign}, "mi": {"-", 2, precAdd}, "ml": {"*", 2, precMul}, "mm": {"--", 1, precPostfix}, "na": {"new[]", 3, precUnary}, "ne": {"!=", 2, precEqual}, "ng": {"-", 1, precUnary}, "nt": {"!", 1, precUnary}, "nw": {"new", 3, precUnary}, "nx": {"noexcept", 1, precUnary}, "oR": {"|=", 2, precAssign}, "oo": {"||", 2, precLogicalOr}, "or": {"|", 2, precOr}, "pL": {"+=", 2, precAssign}, "pl": {"+", 2, precAdd}, "pm": {"->*", 2, precPtrMem}, "pp": {"++", 1, precPostfix}, "ps": {"+", 1, precUnary}, "pt": {"->", 2, precPostfix}, "qu": {"?", 3, precCond}, "rM": {"%=", 2, precAssign}, "rS": {">>=", 2, precAssign}, "rc": {"reinterpret_cast", 2, precPostfix}, "rm": {"%", 2, precMul}, "rs": {">>", 2, precShift}, "sP": {"sizeof...", 1, precUnary}, "sZ": {"sizeof...", 1, precUnary}, "sc": {"static_cast", 2, precPostfix}, "ss": {"<=>", 2, precSpaceship}, "st": {"sizeof ", 1, precUnary}, "sz": {"sizeof ", 1, precUnary}, "te": {"typeid ", 1, precPostfix}, "ti": {"typeid ", 1, precPostfix}, "tr": {"throw", 0, precPrimary}, "tw": {"throw ", 1, precUnary}, } // operatorName parses: // // operator_name ::= many different two character encodings. // ::= cv <type> // ::= v <digit> <source-name> // // 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, precedence: op.prec}, op.args } else { st.failEarlier("unrecognized operator code", 2) panic("not reached") } } // localName parses: // // <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>] // ::= Z <(function) encoding> E s [<discriminator>] // ::= Z <(function) encoding> E d [<parameter> number>] _ <entity name> // // Besides the name, this returns whether it saw the code indicating // a C++23 explicit object parameter. func (st *state) localName() (AST, bool) { 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}, false } else { num := -1 if len(st.str) > 0 && st.str[0] == 'd' { // Default argument scope. st.advance(1) num = st.compactNumber() } n, explicitObjectParameter := st.name() n = st.discriminator(n) if num >= 0 { n = &DefaultArg{Num: num, Arg: n} } return &Qualified{Scope: fn, Name: n, LocalName: true}, explicitObjectParameter } } // 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}} } // specialName parses: // // <special-name> ::= TV <type> // ::= TT <type> // ::= TI <type> // ::= TS <type> // ::= TA <template-arg> // ::= GV <(object) name> // ::= T <call-offset> <(base) encoding> // ::= Tc <call-offset> <call-offset> <(base) encoding> // g++ extensions: // ::= TC <type> <(offset) number> _ <(base) type> // ::= TF <type> // ::= TJ <type> // ::= GR <name> // ::= GA <encoding> // ::= Gr <resource name> // ::= GTt <encoding> // ::= GTn <encoding> // ::= GI <module name> 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 'A': t := st.templateArg(nil) return &Special{Prefix: "template parameter object 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() st.seqID(true) return &Special{Prefix: "reference temporary for ", 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() case 'I': module := st.moduleName(nil) if module == nil { st.fail("expected module after GI") } return &Special{Prefix: "initializer for module ", Val: module} default: st.fail("unrecognized special G name code") panic("not reached") } } } // callOffset parses: // // <call-offset> ::= h <nv-offset> _ // ::= v <v-offset> _ // // <nv-offset> ::= <(offset) number> // // <v-offset> ::= <(offset) number> _ <(virtual offset) number> // // The c parameter, if not 0, is a character we just read which is the // start of the <call-offset>. // // 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': "...", } // demangleType parses: // // <type> ::= <builtin-type> // ::= <function-type> // ::= <class-enum-type> // ::= <array-type> // ::= <pointer-to-member-type> // ::= <template-param> // ::= <template-template-param> <template-args> // ::= <substitution> // ::= <CV-qualifiers> <type> // ::= P <type> // ::= R <type> // ::= O <type> (C++0x) // ::= C <type> // ::= G <type> // ::= U <source-name> <type> // // <builtin-type> ::= various one letter codes // ::= u <source-name> func (st *state) demangleType(isCast bool) AST { if len(st.str) == 0 { st.fail("expected type") } addSubst := true q := st.cvQualifiers() if q != nil { 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 q != nil { 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() if len(st.str) > 0 && st.str[0] == 'I' { st.advance(1) base := st.demangleType(false) if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after transformed type") } st.advance(1) ret = &TransformedType{Name: ret.(*Name).Name, Base: base} } case 'F': ret = st.functionType() case 'N', 'W', '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': if len(st.str) > 1 && (st.str[1] == 's' || st.str[1] == 'u' || st.str[1] == 'e') { c = st.str[1] st.advance(2) ret, _ = st.name() var kind string switch c { case 's': kind = "struct" case 'u': kind = "union" case 'e': kind = "enum" } ret = &ElaboratedType{Kind: kind, Type: ret} break } 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 <class-enum-type>. var c2 byte if len(st.str) > 1 { c2 = st.str[1] } if isDigit(c2) || c2 == '_' || isUpper(c2) { ret = st.substitution(false) if _, ok := ret.(*ModuleName); ok { ret, _ = st.unqualifiedName(ret) st.subs.add(ret) } 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 'c': ret = &Name{Name: "decltype(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 'u': ret = &BuiltinType{Name: "char8_t"} 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 bits := 0 if len(st.str) > 0 && isDigit(st.str[0]) { accum = true bits = st.number() } if len(st.str) > 0 && st.str[0] == '_' { if bits == 0 { st.fail("expected non-zero number of bits") } st.advance(1) ret = &BinaryFP{Bits: bits} } else { 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 case 'B', 'U': signed := c2 == 'B' var size AST if len(st.str) > 0 && isDigit(st.str[0]) { bits := st.number() size = &Name{Name: fmt.Sprintf("%d", bits)} } else { size = st.expression() } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after _BitInt size") } st.advance(1) ret = &BitIntType{Size: size, Signed: signed} case 'k': constraint, _ := st.name() ret = &SuffixType{ Base: constraint, Suffix: "auto", } case 'K': constraint, _ := st.name() ret = &SuffixType{ Base: constraint, Suffix: "decltype(auto)", } 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 q != nil { 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: // // <nested-name> // -> <template-prefix> <template-args> // -> <prefix> <template-unqualified-name> <template-args> // -> <unqualified-name> <template-unqualified-name> <template-args> // -> <source-name> <template-unqualified-name> <template-args> // -> <source-name> <operator-name> <template-args> // -> <source-name> cv <type> <template-args> // -> <source-name> cv <template-template-param> <template-args> <template-args> // // Otherwise, we have this derivation: // // <nested-name> // -> <template-prefix> <template-args> // -> <prefix> <template-unqualified-name> <template-args> // -> <unqualified-name> <template-unqualified-name> <template-args> // -> <source-name> <template-unqualified-name> <template-args> // -> <source-name> <operator-name> <template-args> // -> <source-name> cv <type> <template-args> // -> <source-name> cv <template-param> <template-args> // // 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 <template-param> <template-args> but there is not // another following <template-args>. In that case, we reset the // parse and just return the <template-param>. 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 qualifier lists into one. func mergeQualifiers(q1AST, q2AST AST) AST { if q1AST == nil { return q2AST } if q2AST == nil { return q1AST } q1 := q1AST.(*Qualifiers) m := make(map[string]bool) for _, qualAST := range q1.Qualifiers { qual := qualAST.(*Qualifier) if len(qual.Exprs) == 0 { m[qual.Name] = true } } rq := q1.Qualifiers for _, qualAST := range q2AST.(*Qualifiers).Qualifiers { qual := qualAST.(*Qualifier) if len(qual.Exprs) > 0 { rq = append(rq, qualAST) } else if !m[qual.Name] { rq = append(rq, qualAST) m[qual.Name] = true } } q1.Qualifiers = rq 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", } // cvQualifiers parses: // // <CV-qualifiers> ::= [r] [V] [K] func (st *state) cvQualifiers() AST { var q []AST qualLoop: for len(st.str) > 0 { if qv, ok := qualifiers[st.str[0]]; ok { qual := &Qualifier{Name: qv} q = append([]AST{qual}, q...) st.advance(1) } else if len(st.str) > 1 && st.str[0] == 'D' { var qual AST switch st.str[1] { case 'x': qual = &Qualifier{Name: "transaction_safe"} st.advance(2) case 'o': qual = &Qualifier{Name: "noexcept"} st.advance(2) case 'O': st.advance(2) expr := st.expression() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after computed noexcept expression") } st.advance(1) qual = &Qualifier{Name: "noexcept", Exprs: []AST{expr}} case 'w': st.advance(2) parmlist := st.parmlist(false) if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after throw parameter list") } st.advance(1) qual = &Qualifier{Name: "throw", Exprs: parmlist} default: break qualLoop } q = append([]AST{qual}, q...) } else { break } } if len(q) == 0 { return nil } return &Qualifiers{Qualifiers: q} } // refQualifier parses: // // <ref-qualifier> ::= 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 "" } // parmlist parses: // // <type>+ func (st *state) parmlist(explicitObjectParameter bool) []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 } if st.str[0] == 'Q' { // This is a requires clause. break } ptype := st.demangleType(false) if len(ret) == 0 && explicitObjectParameter { ptype = &ExplicitObjectParameter{Base: ptype} } 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 } // functionType parses: // // <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] 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, false) 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 } // bareFunctionType parses: // // <bare-function-type> ::= [J]<type>+ func (st *state) bareFunctionType(hasReturnType, explicitObjectParameter 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(explicitObjectParameter) return &FunctionType{ Return: returnType, Args: types, ForLocalName: false, // may be set later in encoding } } // arrayType parses: // // <array-type> ::= 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 } // vectorType parses: // // <vector-type> ::= Dv <number> _ <type> // ::= Dv _ <expression> _ <type> 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} } // pointerToMemberType parses: // // <pointer-to-member-type> ::= 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} } // compactNumber parses: // // <non-negative number> _ 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 } // templateParam parses: // // <template-param> ::= T_ // ::= T <(parameter-2 non-negative) number> _ // ::= TL <level-1> __ // ::= TL <level-1> _ <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 { off := st.off str := st.str st.checkChar('T') level := 0 if len(st.str) > 0 && st.str[0] == 'L' { st.advance(1) level = st.compactNumber() } n := st.compactNumber() // We don't try to substitute template parameters in a // constraint expression. if st.parsingConstraint { return &Name{Name: str[:st.off-1-off]} } if level >= len(st.templates) { if st.lambdaTemplateLevel > 0 && level == st.lambdaTemplateLevel-1 { // Lambda auto params are mangled as template params. // See https://gcc.gnu.org/PR78252. return &LambdaAuto{Index: n} } st.failEarlier(fmt.Sprintf("template parameter is not in scope of template (level %d >= %d)", level, len(st.templates)), st.off-off) } template := st.templates[level] 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) { if st.lambdaTemplateLevel > 0 && level == st.lambdaTemplateLevel-1 { // Lambda auto params are mangled as template params. // See https://gcc.gnu.org/PR78252. return &LambdaAuto{Index: n} } 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) { seen := make(map[AST]bool) 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 case *Closure: // There are no template params in closure types. // https://gcc.gnu.org/PR78252. return false default: if seen[a] { return false } seen[a] = true 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) } } // templateArgs parses: // // <template-args> ::= I <template-arg>+ 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) ret = append(ret, arg) if len(st.str) > 0 && st.str[0] == 'Q' { // A list of template arguments can have a // constraint, but we don't demangle it. st.constraintExpr() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected end of template arguments after constraint") } } } st.advance(1) return ret } // templateArg parses: // // <template-arg> ::= <type> // ::= X <expression> E // ::= <expr-primary> // ::= J <template-arg>* E // ::= LZ <encoding> E // ::= <template-param-decl> <template-arg> func (st *state) templateArg(prev []AST) 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} case 'T': var arg byte if len(st.str) > 1 { arg = st.str[1] } switch arg { case 'y', 'n', 't', 'p', 'k': off := st.off // Apparently template references in the // template parameter refer to previous // arguments in the same template. template := &Template{Args: prev} st.templates = append(st.templates, template) param, _ := st.templateParamDecl() st.templates = st.templates[:len(st.templates)-1] if param == nil { st.failEarlier("expected template parameter as template argument", st.off-off) } arg := st.templateArg(nil) return &TemplateParamQualifiedArg{Param: param, Arg: arg} } return st.demangleType(false) 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} } // expression parses: // // <expression> ::= <(unary) operator-name> <expression> // ::= <(binary) operator-name> <expression> <expression> // ::= <(trinary) operator-name> <expression> <expression> <expression> // ::= pp_ <expression> // ::= mm_ <expression> // ::= cl <expression>+ E // ::= cl <expression>+ E // ::= cv <type> <expression> // ::= cv <type> _ <expression>* E // ::= tl <type> <braced-expression>* E // ::= il <braced-expression>* E // ::= [gs] nw <expression>* _ <type> E // ::= [gs] nw <expression>* _ <type> <initializer> // ::= [gs] na <expression>* _ <type> E // ::= [gs] na <expression>* _ <type> <initializer> // ::= [gs] dl <expression> // ::= [gs] da <expression> // ::= dc <type> <expression> // ::= sc <type> <expression> // ::= cc <type> <expression> // ::= mc <parameter type> <expr> [<offset number>] E // ::= rc <type> <expression> // ::= ti <type> // ::= te <expression> // ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E // ::= st <type> // ::= sz <expression> // ::= at <type> // ::= az <expression> // ::= nx <expression> // ::= <template-param> // ::= <function-param> // ::= dt <expression> <unresolved-name> // ::= pt <expression> <unresolved-name> // ::= ds <expression> <expression> // ::= sZ <template-param> // ::= sZ <function-param> // ::= sP <template-arg>* E // ::= sp <expression> // ::= fl <binary operator-name> <expression> // ::= fr <binary operator-name> <expression> // ::= fL <binary operator-name> <expression> <expression> // ::= fR <binary operator-name> <expression> <expression> // ::= tw <expression> // ::= tr // ::= u <source-name> <template-arg>* E // ::= <unresolved-name> // ::= <expr-primary> // // <function-param> ::= fp <CV-qualifiers> _ // ::= fp <CV-qualifiers> <number> // ::= fL <number> p <CV-qualifiers> _ // ::= fL <number> p <CV-qualifiers> <number> // ::= fpT // // <braced-expression> ::= <expression> // ::= di <field source-name> <braced-expression> // ::= dx <index expression> <braced-expression> // ::= dX <range begin expression> <range end expression> <braced-expression> 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] == 'o' { st.advance(2) return st.subobject() } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' { return st.unresolvedName() } 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(nil) 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 { // We can see qualifiers here, but we don't // include them in the demangled string. st.cvQualifiers() index := st.compactNumber() return &FunctionParam{Index: index + 1} } } else if st.str[0] == 'f' && len(st.str) > 2 && st.str[1] == 'L' && isDigit(st.str[2]) { st.advance(2) // We don't include the scope count in the demangled string. st.number() if len(st.str) == 0 || st.str[0] != 'p' { st.fail("expected p after function parameter scope count") } st.advance(1) // We can see qualifiers here, but we don't include them // in the demangled string. st.cvQualifiers() index := st.compactNumber() return &FunctionParam{Index: index + 1} } else if st.str[0] == 'm' && len(st.str) > 1 && st.str[1] == 'c' { st.advance(2) typ := st.demangleType(false) expr := st.expression() offset := 0 if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) { offset = st.number() } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after pointer-to-member conversion") } st.advance(1) return &PtrMemCast{ Type: typ, Expr: expr, Offset: offset, } } 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(nil) 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 st.str[0] == 'u' { st.advance(1) name := st.sourceName() // Special case __uuidof followed by type or // expression, as used by LLVM. if n, ok := name.(*Name); ok && n.Name == "__uuidof" { if len(st.str) < 2 { st.fail("missing uuidof argument") } var operand AST if st.str[0] == 't' { st.advance(1) operand = st.demangleType(false) } else if st.str[0] == 'z' { st.advance(1) operand = st.expression() } if operand != nil { return &Binary{ Op: &Operator{Name: "()"}, Left: name, Right: &ExprList{ Exprs: []AST{operand}, }, } } } var args []AST for { if len(st.str) == 0 { st.fail("missing argument in vendor extended expressoin") } if st.str[0] == 'E' { st.advance(1) break } arg := st.templateArg(nil) args = append(args, arg) } return &Binary{ Op: &Operator{Name: "()"}, Left: name, Right: &ExprList{Exprs: args}, } } else if st.str[0] == 'r' && len(st.str) > 1 && (st.str[1] == 'q' || st.str[1] == 'Q') { return st.requiresExpr() } 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 if code == "di" { left, _ = st.unqualifiedName(nil) } else { left = st.expression() } if code == "cl" || code == "cp" { right = st.exprList('E') } else if code == "dt" || code == "pt" { if len(st.str) > 0 && st.str[0] == 'L' { right = st.exprPrimary() } else { right = st.unresolvedName() 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") } } } // subobject parses: // // <expression> ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E // <union-selector> ::= _ [<number>] func (st *state) subobject() AST { typ := st.demangleType(false) expr := st.expression() offset := 0 if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) { offset = st.number() } var selectors []int for len(st.str) > 0 && st.str[0] == '_' { st.advance(1) selector := 0 if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) { selector = st.number() } selectors = append(selectors, selector) } pastEnd := false if len(st.str) > 0 && st.str[0] == 'p' { st.advance(1) pastEnd = true } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after subobject") } st.advance(1) return &Subobject{ Type: typ, SubExpr: expr, Offset: offset, Selectors: selectors, PastEnd: pastEnd, } } // unresolvedName parses: // // <unresolved-name> ::= [gs] <base-unresolved-name> // ::= sr <unresolved-type> <base-unresolved-name> // ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> // ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> func (st *state) unresolvedName() AST { if len(st.str) >= 2 && st.str[:2] == "gs" { st.advance(2) n := st.unresolvedName() return &Unary{ Op: &Operator{Name: "::"}, Expr: n, Suffix: false, SizeofType: false, } } else if len(st.str) >= 2 && st.str[:2] == "sr" { 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} st.subs.add(n) } 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} } } 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 { return st.baseUnresolvedName() } } // baseUnresolvedName parses: // // <base-unresolved-name> ::= <simple-id> // ::= on <operator-name> // ::= on <operator-name> <template-args> // ::= dn <destructor-name> // // <simple-id> ::= <source-name> [ <template-args> ] 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 } // requiresExpr parses: // // <expression> ::= rQ <bare-function-type> _ <requirement>+ E // ::= rq <requirement>+ E // <requirement> ::= X <expression> [N] [R <type-constraint>] // ::= T <type> // ::= Q <constraint-expression> func (st *state) requiresExpr() AST { st.checkChar('r') if len(st.str) == 0 || (st.str[0] != 'q' && st.str[0] != 'Q') { st.fail("expected q or Q in requires clause in expression") } kind := st.str[0] st.advance(1) var params []AST if kind == 'Q' { for len(st.str) > 0 && st.str[0] != '_' { typ := st.demangleType(false) params = append(params, typ) } st.advance(1) } var requirements []AST for len(st.str) > 0 && st.str[0] != 'E' { var req AST switch st.str[0] { case 'X': st.advance(1) expr := st.expression() var noexcept bool if len(st.str) > 0 && st.str[0] == 'N' { st.advance(1) noexcept = true } var typeReq AST if len(st.str) > 0 && st.str[0] == 'R' { st.advance(1) typeReq, _ = st.name() } req = &ExprRequirement{ Expr: expr, Noexcept: noexcept, TypeReq: typeReq, } case 'T': st.advance(1) typ := st.demangleType(false) req = &TypeRequirement{Type: typ} case 'Q': st.advance(1) // We parse a regular expression rather than a // constraint expression. expr := st.expression() req = &NestedRequirement{Constraint: expr} default: st.fail("unrecognized requirement code") } requirements = append(requirements, req) } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after requirements") } st.advance(1) return &RequiresExpr{ Params: params, Requirements: requirements, } } // exprPrimary parses: // // <expr-primary> ::= L <type> <(value) number> E // ::= L <type> <(value) float> E // ::= L <mangled-name> 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) isArrayType := func(typ AST) bool { if twq, ok := typ.(*TypeWithQualifiers); ok { typ = twq.Base } _, ok := typ.(*ArrayType) return ok } 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' { if bt, ok := t.(*BuiltinType); ok && bt.Name == "decltype(nullptr)" { // A nullptr should not have a value. // We accept one if present because GCC // used to generate one. // https://gcc.gnu.org/PR91979. } else if cl, ok := t.(*Closure); ok { // A closure doesn't have a value. st.advance(1) return &LambdaExpr{Type: cl} } else if isArrayType(t) { st.advance(1) return &StringLiteral{Type: t} } else { 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 } // discriminator parses: // // <discriminator> ::= _ <(non-negative) number> (when number < 10) // __ <(non-negative) number> _ (when number >= 10) func (st *state) discriminator(a AST) AST { if len(st.str) == 0 || st.str[0] != '_' { // clang can generate a discriminator at the end of // the string with no underscore. for i := 0; i < len(st.str); i++ { if !isDigit(st.str[i]) { return a } } // Skip the trailing digits. st.advance(len(st.str)) return a } off := st.off st.advance(1) trailingUnderscore := false if len(st.str) > 0 && st.str[0] == '_' { st.advance(1) trailingUnderscore = true } d := st.number() if d < 0 { st.failEarlier("invalid negative discriminator", st.off-off) } if trailingUnderscore && d >= 10 { if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after discriminator >= 10") } st.advance(1) } // We don't currently print out the discriminator, so we don't // save it. return a } // closureTypeName parses: // // <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ // <lambda-sig> ::= <parameter type>+ func (st *state) closureTypeName() AST { st.checkChar('U') st.checkChar('l') oldLambdaTemplateLevel := st.lambdaTemplateLevel st.lambdaTemplateLevel = len(st.templates) + 1 var templateArgs []AST var template *Template for len(st.str) > 1 && st.str[0] == 'T' { arg, templateVal := st.templateParamDecl() if arg == nil { break } templateArgs = append(templateArgs, arg) if template == nil { template = &Template{ Name: &Name{Name: "lambda"}, } st.templates = append(st.templates, template) } template.Args = append(template.Args, templateVal) } var templateArgsConstraint AST if len(st.str) > 0 && st.str[0] == 'Q' { templateArgsConstraint = st.constraintExpr() } types := st.parmlist(false) st.lambdaTemplateLevel = oldLambdaTemplateLevel if template != nil { st.templates = st.templates[:len(st.templates)-1] } var callConstraint AST if len(st.str) > 0 && st.str[0] == 'Q' { callConstraint = st.constraintExpr() } if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after closure type name") } st.advance(1) num := st.compactNumber() return &Closure{ TemplateArgs: templateArgs, TemplateArgsConstraint: templateArgsConstraint, Types: types, Num: num, CallConstraint: callConstraint, } } // templateParamDecl parses: // // <template-param-decl> ::= Ty # type parameter // ::= Tk <concept name> [<template-args>] # constrained type parameter // ::= Tn <type> # non-type parameter // ::= Tt <template-param-decl>* E # template parameter // ::= Tp <template-param-decl> # parameter pack // // Returns the new AST to include in the AST we are building and the // new AST to add to the list of template parameters. // // Returns nil, nil if not looking at a template-param-decl. func (st *state) templateParamDecl() (AST, AST) { if len(st.str) < 2 || st.str[0] != 'T' { return nil, nil } mk := func(prefix string, p *int) AST { idx := *p (*p)++ return &TemplateParamName{ Prefix: prefix, Index: idx, } } switch st.str[1] { case 'y': st.advance(2) name := mk("$T", &st.typeTemplateParamCount) tp := &TypeTemplateParam{ Name: name, } return tp, name case 'k': // We don't track enclosing template parameter levels. // Don't try to demangle template parameter substitutions // in constraints. hold := st.parsingConstraint st.parsingConstraint = true defer func() { st.parsingConstraint = hold }() st.advance(2) constraint, _ := st.name() name := mk("$T", &st.typeTemplateParamCount) tp := &ConstrainedTypeTemplateParam{ Name: name, Constraint: constraint, } return tp, name case 'n': st.advance(2) name := mk("$N", &st.nonTypeTemplateParamCount) typ := st.demangleType(false) tp := &NonTypeTemplateParam{ Name: name, Type: typ, } return tp, name case 't': st.advance(2) name := mk("$TT", &st.templateTemplateParamCount) var params []AST var template *Template var constraint AST for { if len(st.str) == 0 { st.fail("expected closure template parameter") } if st.str[0] == 'E' { st.advance(1) break } off := st.off param, templateVal := st.templateParamDecl() if param == nil { st.failEarlier("expected closure template parameter", st.off-off) } params = append(params, param) if template == nil { template = &Template{ Name: &Name{Name: "template_template"}, } st.templates = append(st.templates, template) } template.Args = append(template.Args, templateVal) if len(st.str) > 0 && st.str[0] == 'Q' { // A list of template template // parameters can have a constraint. constraint = st.constraintExpr() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected end of template template parameters after constraint") } } } if template != nil { st.templates = st.templates[:len(st.templates)-1] } tp := &TemplateTemplateParam{ Name: name, Params: params, Constraint: constraint, } return tp, name case 'p': st.advance(2) off := st.off param, templateVal := st.templateParamDecl() if param == nil { st.failEarlier("expected lambda template parameter", st.off-off) } return &TemplateParamPack{Param: param}, templateVal default: return nil, nil } } // unnamedTypeName parses: // // <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ func (st *state) unnamedTypeName() AST { st.checkChar('U') st.checkChar('t') num := st.compactNumber() ret := &UnnamedType{Num: num} st.subs.add(ret) return ret } // constraintExpr parses a constraint expression. This is just a // regular expression, but template parameters are handled specially. func (st *state) constraintExpr() AST { st.checkChar('Q') hold := st.parsingConstraint st.parsingConstraint = true defer func() { st.parsingConstraint = hold }() return st.expression() } // 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]) || isDigit(st.str[1]) || st.str[1] == '_') { i += 2 for len(st.str) > i && (isLower(st.str[i]) || isDigit(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<char, std::char_traits<char>, std::allocator<char> > '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<char, std::char_traits<char> > '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<char, std::char_traits<char> > '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<char, std::char_traits<char> > '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"}}}}}, } // substitution parses: // // <substitution> ::= S <seq-id> _ // ::= 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] off := st.off if c == '_' || isDigit(c) || isUpper(c) { id := st.seqID(false) if id >= len(st.subs) { st.failEarlier(fmt.Sprintf("substitution index out of range (%d >= %d)", id, len(st.subs)), st.off-off) } ret := st.subs[id] // We need to update any references to template // parameters to refer to the currently active // template. // When copying a Typed we may need to adjust // the templates. copyTemplates := st.templates var oldLambdaTemplateLevel []int // pushTemplate is called from skip, popTemplate from copy. pushTemplate := func(template *Template) { copyTemplates = append(copyTemplates, template) oldLambdaTemplateLevel = append(oldLambdaTemplateLevel, st.lambdaTemplateLevel) st.lambdaTemplateLevel = 0 } popTemplate := func() { copyTemplates = copyTemplates[:len(copyTemplates)-1] st.lambdaTemplateLevel = oldLambdaTemplateLevel[len(oldLambdaTemplateLevel)-1] oldLambdaTemplateLevel = oldLambdaTemplateLevel[:len(oldLambdaTemplateLevel)-1] } copy := func(a AST) AST { var index int switch a := a.(type) { case *Typed: // Remove the template added in skip. if _, ok := a.Name.(*Template); ok { popTemplate() } return nil case *Closure: // Undo the save in skip. st.lambdaTemplateLevel = oldLambdaTemplateLevel[len(oldLambdaTemplateLevel)-1] oldLambdaTemplateLevel = oldLambdaTemplateLevel[:len(oldLambdaTemplateLevel)-1] return nil case *TemplateParam: index = a.Index case *LambdaAuto: // A lambda auto parameter is represented // as a template parameter, so we may have // to change back when substituting. index = a.Index default: return nil } if st.parsingConstraint { // We don't try to substitute template // parameters in a constraint expression. return &Name{Name: fmt.Sprintf("T%d", index)} } if st.lambdaTemplateLevel > 0 { if _, ok := a.(*LambdaAuto); ok { return nil } return &LambdaAuto{Index: index} } var template *Template if len(copyTemplates) > 0 { template = copyTemplates[len(copyTemplates)-1] } else if rt, ok := ret.(*Template); ok { // At least with clang we can see a template // to start, and sometimes we need to refer // to it. There is probably something wrong // here. template = rt } else { st.failEarlier("substituted template parameter not in scope of template", st.off-off) } if template == nil { // This template parameter is within // the scope of a cast operator. return &TemplateParam{Index: index, Template: nil} } if index >= len(template.Args) { st.failEarlier(fmt.Sprintf("substituted template index out of range (%d >= %d)", index, len(template.Args)), st.off-off) } return &TemplateParam{Index: index, Template: template} } seen := make(map[AST]bool) skip := func(a AST) bool { switch a := a.(type) { case *Typed: if template, ok := a.Name.(*Template); ok { // This template is removed in copy. pushTemplate(template) } return false case *Closure: // This is undone in copy. oldLambdaTemplateLevel = append(oldLambdaTemplateLevel, st.lambdaTemplateLevel) st.lambdaTemplateLevel = len(copyTemplates) + 1 return false case *TemplateParam, *LambdaAuto: return false } if seen[a] { return true } seen[a] = true return false } if c := ret.Copy(copy, skip); c != nil { return c } return ret } else { st.advance(1) 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) st.subs.add(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 { seen := make(map[AST]bool) skip := func(a AST) bool { if seen[a] { return true } seen[a] = true 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 } seen := make(map[AST]bool) skip := func(sub AST) bool { // Don't traverse into another // pack expansion. if _, ok := sub.(*PackExpansion); ok { return true } if seen[sub] { return true } seen[sub] = true 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 { seen := make(map[AST]bool) 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 } if seen[a] { return false } seen[a] = true return true }) return ret }
// Copyright 2021 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 import ( "fmt" "math" "math/bits" "strings" "unicode/utf8" ) // rustToString demangles a Rust symbol. func rustToString(name string, options []Option) (ret string, err error) { if !strings.HasPrefix(name, "_R") { return "", ErrNotMangledName } // 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 = "" err = de return } panic(r) } }() suffix := "" dot := strings.Index(name, ".") if dot >= 0 { suffix = name[dot:] name = name[:dot] } name = name[2:] rst := &rustState{orig: name, str: name} for _, o := range options { if o == NoTemplateParams { rst.noGenericArgs = true } else if isMaxLength(o) { rst.max = maxLength(o) } } rst.symbolName() if len(rst.str) > 0 { rst.fail("unparsed characters at end of mangled name") } if suffix != "" { llvmStyle := false for _, o := range options { if o == LLVMStyle { llvmStyle = true break } } if llvmStyle { rst.skip = false rst.writeString(" (") rst.writeString(suffix) rst.writeByte(')') } } s := rst.buf.String() if rst.max > 0 && len(s) > rst.max { s = s[:rst.max] } return s, nil } // A rustState holds the current state of demangling a Rust string. type rustState struct { orig string // the original string being demangled str string // remainder of string to demangle off int // offset of str within original string buf strings.Builder // demangled string being built skip bool // don't print, just skip lifetimes int64 // number of bound lifetimes last byte // last byte written to buffer noGenericArgs bool // don't demangle generic arguments max int // maximum output length } // fail panics with demangleErr, to be caught in rustToString. func (rst *rustState) fail(err string) { panic(demangleErr{err: err, off: rst.off}) } // advance advances the current string offset. func (rst *rustState) advance(add int) { if len(rst.str) < add { panic("internal error") } rst.str = rst.str[add:] rst.off += add } // checkChar requires that the next character in the string be c, // and advances past it. func (rst *rustState) checkChar(c byte) { if len(rst.str) == 0 || rst.str[0] != c { rst.fail("expected " + string(c)) } rst.advance(1) } // writeByte writes a byte to the buffer. func (rst *rustState) writeByte(c byte) { if rst.skip { return } if rst.max > 0 && rst.buf.Len() > rst.max { rst.skip = true return } rst.last = c rst.buf.WriteByte(c) } // writeString writes a string to the buffer. func (rst *rustState) writeString(s string) { if rst.skip { return } if rst.max > 0 && rst.buf.Len() > rst.max { rst.skip = true return } if len(s) > 0 { rst.last = s[len(s)-1] rst.buf.WriteString(s) } } // symbolName parses: // // <symbol-name> = "_R" [<decimal-number>] <path> [<instantiating-crate>] // <instantiating-crate> = <path> // // We've already skipped the "_R". func (rst *rustState) symbolName() { if len(rst.str) < 1 { rst.fail("expected symbol-name") } if isDigit(rst.str[0]) { rst.fail("unsupported Rust encoding version") } rst.path(true) if len(rst.str) > 0 { rst.skip = true rst.path(false) } } // path parses: // // <path> = "C" <identifier> // crate root // | "M" <impl-path> <type> // <T> (inherent impl) // | "X" <impl-path> <type> <path> // <T as Trait> (trait impl) // | "Y" <type> <path> // <T as Trait> (trait definition) // | "N" <namespace> <path> <identifier> // ...::ident (nested path) // | "I" <path> {<generic-arg>} "E" // ...<T, U> (generic args) // | <backref> // <namespace> = "C" // closure // | "S" // shim // | <A-Z> // other special namespaces // | <a-z> // internal namespaces // // needsSeparator is true if we need to write out :: for a generic; // it is passed as false if we are in the middle of a type. func (rst *rustState) path(needsSeparator bool) { if len(rst.str) < 1 { rst.fail("expected path") } switch c := rst.str[0]; c { case 'C': rst.advance(1) _, ident := rst.identifier() rst.writeString(ident) case 'M', 'X': rst.advance(1) rst.implPath() rst.writeByte('<') rst.demangleType() if c == 'X' { rst.writeString(" as ") rst.path(false) } rst.writeByte('>') case 'Y': rst.advance(1) rst.writeByte('<') rst.demangleType() rst.writeString(" as ") rst.path(false) rst.writeByte('>') case 'N': rst.advance(1) if len(rst.str) < 1 { rst.fail("expected namespace") } ns := rst.str[0] switch { case ns >= 'a' && ns <= 'z': case ns >= 'A' && ns <= 'Z': default: rst.fail("invalid namespace character") } rst.advance(1) rst.path(needsSeparator) dis, ident := rst.identifier() if ns >= 'A' && ns <= 'Z' { rst.writeString("::{") switch ns { case 'C': rst.writeString("closure") case 'S': rst.writeString("shim") default: rst.writeByte(ns) } if len(ident) > 0 { rst.writeByte(':') rst.writeString(ident) } if !rst.skip { fmt.Fprintf(&rst.buf, "#%d}", dis) rst.last = '}' } } else { rst.writeString("::") rst.writeString(ident) } case 'I': rst.advance(1) rst.path(needsSeparator) if needsSeparator { rst.writeString("::") } rst.writeByte('<') rst.genericArgs() rst.writeByte('>') rst.checkChar('E') case 'B': rst.backref(func() { rst.path(needsSeparator) }) default: rst.fail("unrecognized letter in path") } } // implPath parses: // // <impl-path> = [<disambiguator>] <path> func (rst *rustState) implPath() { // This path is not part of the demangled string. hold := rst.skip rst.skip = true defer func() { rst.skip = hold }() rst.disambiguator() rst.path(false) } // identifier parses: // // <identifier> = [<disambiguator>] <undisambiguated-identifier> // // It returns the disambiguator and the identifier. func (rst *rustState) identifier() (int64, string) { dis := rst.disambiguator() ident, _ := rst.undisambiguatedIdentifier() return dis, ident } // disambiguator parses an optional: // // <disambiguator> = "s" <base-62-number> func (rst *rustState) disambiguator() int64 { if len(rst.str) == 0 || rst.str[0] != 's' { return 0 } rst.advance(1) return rst.base62Number() + 1 } // undisambiguatedIdentifier parses: // // <undisambiguated-identifier> = ["u"] <decimal-number> ["_"] <bytes> func (rst *rustState) undisambiguatedIdentifier() (id string, isPunycode bool) { isPunycode = false if len(rst.str) > 0 && rst.str[0] == 'u' { rst.advance(1) isPunycode = true } val := rst.decimalNumber() if len(rst.str) > 0 && rst.str[0] == '_' { rst.advance(1) } if len(rst.str) < val { rst.fail("not enough characters for identifier") } id = rst.str[:val] rst.advance(val) for i := 0; i < len(id); i++ { c := id[i] switch { case c >= '0' && c <= '9': case c >= 'A' && c <= 'Z': case c >= 'a' && c <= 'z': case c == '_': default: rst.fail("invalid character in identifier") } } if isPunycode { id = rst.expandPunycode(id) } return id, isPunycode } // expandPunycode decodes the Rust version of punycode. // This algorithm is taken from RFC 3492 section 6.2. func (rst *rustState) expandPunycode(s string) string { const ( base = 36 tmin = 1 tmax = 26 skew = 38 damp = 700 initialBias = 72 initialN = 128 ) var ( output []rune encoding string ) idx := strings.LastIndex(s, "_") if idx >= 0 { output = []rune(s[:idx]) encoding = s[idx+1:] } else { encoding = s } i := 0 n := initialN bias := initialBias pos := 0 for pos < len(encoding) { oldI := i w := 1 for k := base; ; k += base { if pos == len(encoding) { rst.fail("unterminated punycode") } var digit byte d := encoding[pos] pos++ switch { case '0' <= d && d <= '9': digit = d - '0' + 26 case 'A' <= d && d <= 'Z': digit = d - 'A' case 'a' <= d && d <= 'z': digit = d - 'a' default: rst.fail("invalid punycode digit") } i += int(digit) * w if i < 0 { rst.fail("punycode number overflow") } var t int if k <= bias { t = tmin } else if k > bias+tmax { t = tmax } else { t = k - bias } if int(digit) < t { break } if w >= math.MaxInt32/base { rst.fail("punycode number overflow") } w *= base - t } delta := i - oldI numPoints := len(output) + 1 firstTime := oldI == 0 if firstTime { delta /= damp } else { delta /= 2 } delta += delta / numPoints k := 0 for delta > ((base-tmin)*tmax)/2 { delta /= base - tmin k += base } bias = k + ((base-tmin+1)*delta)/(delta+skew) n += i / (len(output) + 1) if n > utf8.MaxRune { rst.fail("punycode rune overflow") } else if !utf8.ValidRune(rune(n)) { rst.fail("punycode invalid code point") } i %= len(output) + 1 output = append(output, 0) copy(output[i+1:], output[i:]) output[i] = rune(n) i++ } return string(output) } // genericArgs prints a list of generic arguments, without angle brackets. func (rst *rustState) genericArgs() { if rst.noGenericArgs { hold := rst.skip rst.skip = true defer func() { rst.skip = hold }() } first := true for len(rst.str) > 0 && rst.str[0] != 'E' { if first { first = false } else { rst.writeString(", ") } rst.genericArg() } } // genericArg parses: // // <generic-arg> = <lifetime> // | <type> // | "K" <const> // forward-compat for const generics // <lifetime> = "L" <base-62-number> func (rst *rustState) genericArg() { if len(rst.str) < 1 { rst.fail("expected generic-arg") } if rst.str[0] == 'L' { rst.advance(1) rst.writeLifetime(rst.base62Number()) } else if rst.str[0] == 'K' { rst.advance(1) rst.demangleConst() } else { rst.demangleType() } } // binder parses an optional: // // <binder> = "G" <base-62-number> func (rst *rustState) binder() { if len(rst.str) < 1 || rst.str[0] != 'G' { return } rst.advance(1) binderLifetimes := rst.base62Number() + 1 // Every bound lifetime should be referenced later. if binderLifetimes >= int64(len(rst.str))-rst.lifetimes { rst.fail("binder lifetimes overflow") } rst.writeString("for<") for i := int64(0); i < binderLifetimes; i++ { if i > 0 { rst.writeString(", ") } rst.lifetimes++ rst.writeLifetime(1) } rst.writeString("> ") } // demangleType parses: // // <type> = <basic-type> // | <path> // named type // | "A" <type> <const> // [T; N] // | "S" <type> // [T] // | "T" {<type>} "E" // (T1, T2, T3, ...) // | "R" [<lifetime>] <type> // &T // | "Q" [<lifetime>] <type> // &mut T // | "P" <type> // *const T // | "O" <type> // *mut T // | "F" <fn-sig> // fn(...) -> ... // | "D" <dyn-bounds> <lifetime> // dyn Trait<Assoc = X> + Send + 'a // | <backref> func (rst *rustState) demangleType() { if len(rst.str) < 1 { rst.fail("expected type") } c := rst.str[0] if c >= 'a' && c <= 'z' { rst.basicType() return } switch c { case 'C', 'M', 'X', 'Y', 'N', 'I': rst.path(false) case 'A', 'S': rst.advance(1) rst.writeByte('[') rst.demangleType() if c == 'A' { rst.writeString("; ") rst.demangleConst() } rst.writeByte(']') case 'T': rst.advance(1) rst.writeByte('(') c := 0 for len(rst.str) > 0 && rst.str[0] != 'E' { if c > 0 { rst.writeString(", ") } c++ rst.demangleType() } if c == 1 { rst.writeByte(',') } rst.writeByte(')') rst.checkChar('E') case 'R', 'Q': rst.advance(1) rst.writeByte('&') if len(rst.str) > 0 && rst.str[0] == 'L' { rst.advance(1) if lifetime := rst.base62Number(); lifetime > 0 { rst.writeLifetime(lifetime) rst.writeByte(' ') } } if c == 'Q' { rst.writeString("mut ") } rst.demangleType() case 'P': rst.advance(1) rst.writeString("*const ") rst.demangleType() case 'O': rst.advance(1) rst.writeString("*mut ") rst.demangleType() case 'F': rst.advance(1) hold := rst.lifetimes rst.fnSig() rst.lifetimes = hold case 'D': rst.advance(1) hold := rst.lifetimes rst.dynBounds() rst.lifetimes = hold if len(rst.str) == 0 || rst.str[0] != 'L' { rst.fail("expected L") } rst.advance(1) if lifetime := rst.base62Number(); lifetime > 0 { if rst.last != ' ' { rst.writeByte(' ') } rst.writeString("+ ") rst.writeLifetime(lifetime) } case 'B': rst.backref(rst.demangleType) default: rst.fail("unrecognized character in type") } } var rustBasicTypes = map[byte]string{ 'a': "i8", 'b': "bool", 'c': "char", 'd': "f64", 'e': "str", 'f': "f32", 'h': "u8", 'i': "isize", 'j': "usize", 'l': "i32", 'm': "u32", 'n': "i128", 'o': "u128", 'p': "_", 's': "i16", 't': "u16", 'u': "()", 'v': "...", 'x': "i64", 'y': "u64", 'z': "!", } // basicType parses: // // <basic-type> func (rst *rustState) basicType() { if len(rst.str) < 1 { rst.fail("expected basic type") } str, ok := rustBasicTypes[rst.str[0]] if !ok { rst.fail("unrecognized basic type character") } rst.advance(1) rst.writeString(str) } // fnSig parses: // // <fn-sig> = [<binder>] ["U"] ["K" <abi>] {<type>} "E" <type> // <abi> = "C" // | <undisambiguated-identifier> func (rst *rustState) fnSig() { rst.binder() if len(rst.str) > 0 && rst.str[0] == 'U' { rst.advance(1) rst.writeString("unsafe ") } if len(rst.str) > 0 && rst.str[0] == 'K' { rst.advance(1) if len(rst.str) > 0 && rst.str[0] == 'C' { rst.advance(1) rst.writeString(`extern "C" `) } else { rst.writeString(`extern "`) id, isPunycode := rst.undisambiguatedIdentifier() if isPunycode { rst.fail("punycode used in ABI string") } id = strings.ReplaceAll(id, "_", "-") rst.writeString(id) rst.writeString(`" `) } } rst.writeString("fn(") first := true for len(rst.str) > 0 && rst.str[0] != 'E' { if first { first = false } else { rst.writeString(", ") } rst.demangleType() } rst.checkChar('E') rst.writeByte(')') if len(rst.str) > 0 && rst.str[0] == 'u' { rst.advance(1) } else { rst.writeString(" -> ") rst.demangleType() } } // dynBounds parses: // // <dyn-bounds> = [<binder>] {<dyn-trait>} "E" func (rst *rustState) dynBounds() { rst.writeString("dyn ") rst.binder() first := true for len(rst.str) > 0 && rst.str[0] != 'E' { if first { first = false } else { rst.writeString(" + ") } rst.dynTrait() } rst.checkChar('E') } // dynTrait parses: // // <dyn-trait> = <path> {<dyn-trait-assoc-binding>} // <dyn-trait-assoc-binding> = "p" <undisambiguated-identifier> <type> func (rst *rustState) dynTrait() { started := rst.pathStartGenerics() for len(rst.str) > 0 && rst.str[0] == 'p' { rst.advance(1) if started { rst.writeString(", ") } else { rst.writeByte('<') started = true } id, _ := rst.undisambiguatedIdentifier() rst.writeString(id) rst.writeString(" = ") rst.demangleType() } if started { rst.writeByte('>') } } // pathStartGenerics is like path but if it sees an I to start generic // arguments it won't close them. It reports whether it started generics. func (rst *rustState) pathStartGenerics() bool { if len(rst.str) < 1 { rst.fail("expected path") } switch rst.str[0] { case 'I': rst.advance(1) rst.path(false) rst.writeByte('<') rst.genericArgs() rst.checkChar('E') return true case 'B': var started bool rst.backref(func() { started = rst.pathStartGenerics() }) return started default: rst.path(false) return false } } // writeLifetime writes out a lifetime binding. func (rst *rustState) writeLifetime(lifetime int64) { rst.writeByte('\'') if lifetime == 0 { rst.writeByte('_') return } depth := rst.lifetimes - lifetime if depth < 0 { rst.fail("invalid lifetime") } else if depth < 26 { rst.writeByte('a' + byte(depth)) } else { rst.writeByte('z') if !rst.skip { fmt.Fprintf(&rst.buf, "%d", depth-26+1) rst.last = '0' } } } // demangleConst parses: // // <const> = <type> <const-data> // | "p" // placeholder, shown as _ // | <backref> // <const-data> = ["n"] {<hex-digit>} "_" func (rst *rustState) demangleConst() { if len(rst.str) < 1 { rst.fail("expected constant") } if rst.str[0] == 'B' { rst.backref(rst.demangleConst) return } if rst.str[0] == 'p' { rst.advance(1) rst.writeByte('_') return } typ := rst.str[0] const ( invalid = iota signedInt unsignedInt boolean character ) var kind int switch typ { case 'a', 's', 'l', 'x', 'n', 'i': kind = signedInt case 'h', 't', 'm', 'y', 'o', 'j': kind = unsignedInt case 'b': kind = boolean case 'c': kind = character default: rst.fail("unrecognized constant type") } rst.advance(1) if kind == signedInt && len(rst.str) > 0 && rst.str[0] == 'n' { rst.advance(1) rst.writeByte('-') } start := rst.str digits := 0 val := uint64(0) digitLoop: for len(rst.str) > 0 { c := rst.str[0] var digit uint64 switch { case c >= '0' && c <= '9': digit = uint64(c - '0') case c >= 'a' && c <= 'f': digit = uint64(c - 'a' + 10) case c == '_': rst.advance(1) break digitLoop default: rst.fail("expected hex digit or _") } rst.advance(1) if val == 0 && digit == 0 && (len(rst.str) == 0 || rst.str[0] != '_') { rst.fail("invalid leading 0 in constant") } val *= 16 val += digit digits++ } if digits == 0 { rst.fail("expected constant") } switch kind { case signedInt, unsignedInt: if digits > 16 { // Value too big, just write out the string. rst.writeString("0x") rst.writeString(start[:digits]) } else { if !rst.skip { fmt.Fprintf(&rst.buf, "%d", val) rst.last = '0' } } case boolean: if digits > 1 { rst.fail("boolean value too large") } else if val == 0 { rst.writeString("false") } else if val == 1 { rst.writeString("true") } else { rst.fail("invalid boolean value") } case character: if digits > 6 { rst.fail("character value too large") } rst.writeByte('\'') if val == '\t' { rst.writeString(`\t`) } else if val == '\r' { rst.writeString(`\r`) } else if val == '\n' { rst.writeString(`\n`) } else if val == '\\' { rst.writeString(`\\`) } else if val == '\'' { rst.writeString(`\'`) } else if val >= ' ' && val <= '~' { // printable ASCII character rst.writeByte(byte(val)) } else { if !rst.skip { fmt.Fprintf(&rst.buf, `\u{%x}`, val) rst.last = '}' } } rst.writeByte('\'') default: panic("internal error") } } // base62Number parses: // // <base-62-number> = {<0-9a-zA-Z>} "_" func (rst *rustState) base62Number() int64 { if len(rst.str) > 0 && rst.str[0] == '_' { rst.advance(1) return 0 } val := int64(0) for len(rst.str) > 0 { c := rst.str[0] rst.advance(1) if c == '_' { return val + 1 } val *= 62 if c >= '0' && c <= '9' { val += int64(c - '0') } else if c >= 'a' && c <= 'z' { val += int64(c - 'a' + 10) } else if c >= 'A' && c <= 'Z' { val += int64(c - 'A' + 36) } else { rst.fail("invalid digit in base 62 number") } } rst.fail("expected _ after base 62 number") return 0 } // backref parses: // // <backref> = "B" <base-62-number> func (rst *rustState) backref(demangle func()) { backoff := rst.off rst.checkChar('B') idx64 := rst.base62Number() if rst.skip { return } if rst.max > 0 && rst.buf.Len() > rst.max { return } idx := int(idx64) if int64(idx) != idx64 { rst.fail("backref index overflow") } if idx < 0 || idx >= backoff { rst.fail("invalid backref index") } holdStr := rst.str holdOff := rst.off rst.str = rst.orig[idx:backoff] rst.off = idx defer func() { rst.str = holdStr rst.off = holdOff }() demangle() } func (rst *rustState) decimalNumber() int { if len(rst.str) == 0 { rst.fail("expected number") } val := 0 for len(rst.str) > 0 && isDigit(rst.str[0]) { add := int(rst.str[0] - '0') if val >= math.MaxInt32/10-add { rst.fail("decimal number overflow") } val *= 10 val += add rst.advance(1) } return val } // oldRustToString demangles a Rust symbol using the old demangling. // The second result reports whether this is a valid Rust mangled name. func oldRustToString(name string, options []Option) (string, bool) { max := 0 for _, o := range options { if isMaxLength(o) { max = maxLength(o) } } // We know that the string starts with _ZN. name = name[3:] hexDigit := func(c byte) (byte, bool) { switch { case c >= '0' && c <= '9': return c - '0', true case c >= 'a' && c <= 'f': return c - 'a' + 10, true default: return 0, false } } // We know that the strings end with "17h" followed by 16 characters // followed by "E". We check that the 16 characters are all hex digits. // Also the hex digits must contain at least 5 distinct digits. seen := uint16(0) for i := len(name) - 17; i < len(name)-1; i++ { digit, ok := hexDigit(name[i]) if !ok { return "", false } seen |= 1 << digit } if bits.OnesCount16(seen) < 5 { return "", false } name = name[:len(name)-20] // The name is a sequence of length-preceded identifiers. var sb strings.Builder for len(name) > 0 { if max > 0 && sb.Len() > max { break } if !isDigit(name[0]) { return "", false } val := 0 for len(name) > 0 && isDigit(name[0]) { add := int(name[0] - '0') if val >= math.MaxInt32/10-add { return "", false } val *= 10 val += add name = name[1:] } // An optional trailing underscore can separate the // length from the identifier. if len(name) > 0 && name[0] == '_' { name = name[1:] val-- } if len(name) < val { return "", false } id := name[:val] name = name[val:] if sb.Len() > 0 { sb.WriteString("::") } // Ignore leading underscores preceding escape sequences. if strings.HasPrefix(id, "_$") { id = id[1:] } // The identifier can have escape sequences. escape: for len(id) > 0 { switch c := id[0]; c { case '$': codes := map[string]byte{ "SP": '@', "BP": '*', "RF": '&', "LT": '<', "GT": '>', "LP": '(', "RP": ')', } valid := true if len(id) > 2 && id[1] == 'C' && id[2] == '$' { sb.WriteByte(',') id = id[3:] } else if len(id) > 4 && id[1] == 'u' && id[4] == '$' { dig1, ok1 := hexDigit(id[2]) dig2, ok2 := hexDigit(id[3]) val := (dig1 << 4) | dig2 if !ok1 || !ok2 || dig1 > 7 || val < ' ' { valid = false } else { sb.WriteByte(val) id = id[5:] } } else if len(id) > 3 && id[3] == '$' { if code, ok := codes[id[1:3]]; !ok { valid = false } else { sb.WriteByte(code) id = id[4:] } } else { valid = false } if !valid { sb.WriteString(id) break escape } case '.': if strings.HasPrefix(id, "..") { sb.WriteString("::") id = id[2:] } else { sb.WriteByte(c) id = id[1:] } default: sb.WriteByte(c) id = id[1:] } } } s := sb.String() if max > 0 && len(s) > max { s = s[:max] } return s, true }