Файловый менеджер - Редактировать - /var/www/html/demangle.zip
Ðазад
PK ! �k k README.mdnu �[��� # github.com/ianlancetaylor/demangle A Go package that can be used to demangle C++ and Rust symbol names. PK ! 8��:L L ast.gonu �[��� // 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, } 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 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] } // 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) } // 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: ")) } // 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(' ') } ps.writeByte('<') first := true for _, a := range t.Args { if ps.isEmpty(a) { continue } if !first { ps.writeString(", ") } ps.print(a) first = false } if ps.last == '>' { // Avoid syntactic ambiguity in old versions of C++. ps.writeByte(' ') } ps.writeByte('>') } 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) } // 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.writeByte('(') 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.writeByte(')') } } 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) } // 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, "")) } // 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.writeString(" (") ps.printInner(false) ps.writeByte(')') } } 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.writeByte('(') } save := ps.printInner(true) if paren { ps.writeByte(')') } ps.writeByte('(') 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.writeByte(')') 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) } // 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) } // 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.writeString("(") ps.print(dt.Expr) ps.writeByte(')') } 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 } 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) } // 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.print(sp.Pack) ps.writeByte(')') } 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 } func (ttp *TemplateTemplateParam) print(ps *printState) { ps.writeString("template<") for i, param := range ttp.Params { if i > 0 { ps.writeString(", ") } ps.print(param) } ps.writeString("> typename ") ps.print(ttp.Name) } func (ttp *TemplateTemplateParam) Traverse(fn func(AST) bool) { if fn(ttp) { ttp.Name.Traverse(fn) for _, param := range ttp.Params { param.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 } } if !changed { return fn(ttp) } ttp = &TemplateTemplateParam{ Name: name, Params: params, } 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))) } return fmt.Sprintf("%*s%sTemplateTemplateParam:\n%s\n%s", indent, "", field, ttp.Name.goString(indent+2, "Name: "), params.String()) } // 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: ")) } // 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.writeByte('(') } ps.print(val) if paren { ps.writeByte(')') } } // 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 { 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.writeByte('(') ps.print(c.To) ps.writeByte(')') } 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.writeByte('(') ps.print(expr) ps.writeByte(')') } else if op != nil && op.Name == "__alignof__" { // Always use parentheses for __alignof__ argument. ps.writeByte('(') ps.print(expr) ps.writeByte(')') } else if ps.llvmStyle { if op == nil || (op.Name != `operator"" ` && !isDelete) { ps.writeByte('(') } ps.print(expr) if op == nil || (op.Name != `operator"" ` && !isDelete) { ps.writeByte(')') } } 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: ")) } // 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) ps.writeByte('<') ps.print(b.Left) ps.writeString(">(") ps.print(b.Right) ps.writeByte(')') 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. if op != nil && op.Name == ">" { ps.writeByte('(') } left := b.Left skipParens := false skipBothParens := false addSpaces := ps.llvmStyle if ps.llvmStyle && op != nil { switch op.Name { case ".", "->": skipBothParens = true addSpaces = false case "->*": skipParens = true 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 || skipBothParens { ps.print(left) } else if ps.llvmStyle { ps.writeByte('(') ps.print(left) ps.writeByte(')') } 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 { ps.writeByte(' ') } ps.writeString(op.Name) if addSpaces { ps.writeByte(' ') } } } else { ps.print(b.Op) } if skipBothParens { ps.print(b.Right) } else if ps.llvmStyle { ps.writeByte('(') ps.print(b.Right) ps.writeByte(')') } else { parenthesize(ps, b.Right) } if op != nil && op.Name == ">" { ps.writeByte(')') } } 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: ")) } // 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 } parenthesize(ps, t.First) if ps.llvmStyle { ps.writeString(" ? ") } else { ps.writeByte('?') } parenthesize(ps, t.Second) ps.writeString(" : ") 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 _, ok := a.(*ArgumentPack); ok || !ps.llvmStyle { parenthesize(ps, a) } else { ps.print(a) } } if f.Arg2 == nil { if f.Left { ps.writeString("(...") printOp() foldParenthesize(f.Arg1) ps.writeString(")") } else { ps.writeString("(") foldParenthesize(f.Arg1) printOp() ps.writeString("...)") } } else { ps.writeString("(") foldParenthesize(f.Arg1) printOp() ps.writeString("...") printOp() foldParenthesize(f.Arg2) ps.writeString(")") } } 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.writeString("(") ps.print(pmc.Type) ps.writeString(")(") ps.print(pmc.Expr) ps.writeString(")") } 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 == "" { if ps.llvmStyle { ps.writeString("nullptr") } else { ps.print(l.Type) } return } else { isFloat = builtinTypeFloat[b.Name] } } ps.writeByte('(') ps.print(l.Type) ps.writeByte(')') 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) } // 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) { for i, e := range el.Exprs { if i > 0 { ps.writeString(", ") } ps.print(e) } } 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 } // 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 Types []AST Num int } 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 { ps.writeString("<") for i, a := range cl.TemplateArgs { if i > 0 { ps.writeString(", ") } ps.print(a) } ps.writeString(">") } ps.writeString("(") for i, t := range cl.Types { if i > 0 { ps.writeString(", ") } ps.print(t) } ps.writeString(")") } func (cl *Closure) Traverse(fn func(AST) bool) { if fn(cl) { for _, a := range cl.TemplateArgs { a.Traverse(fn) } for _, t := range cl.Types { t.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 } } 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 } } if !changed { return fn(cl) } cl = &Closure{TemplateArgs: args, Types: types, Num: cl.Num} 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 string if len(cl.TemplateArgs) == 0 { args = fmt.Sprintf("%*sTemplateArgs: nil", indent+2, "") } else { args = fmt.Sprintf("%*sTemplateArgs:", indent+2, "") for i, a := range cl.TemplateArgs { args += "\n" args += a.goString(indent+4, fmt.Sprintf("%d: ", i)) } } var types string if len(cl.Types) == 0 { types = fmt.Sprintf("%*sTypes: nil", indent+2, "") } else { types = fmt.Sprintf("%*sTypes:", indent+2, "") for i, t := range cl.Types { types += "\n" types += t.goString(indent+4, fmt.Sprintf("%d: ", i)) } } return fmt.Sprintf("%*s%sClosure: Num: %d\n%s\n%s", indent, "", field, cl.Num, args, types) } // StructuredBindings is a structured binding declaration. type StructuredBindings struct { Bindings []AST } func (sb *StructuredBindings) print(ps *printState) { ps.writeString("[") for i, b := range sb.Bindings { if i > 0 { ps.writeString(", ") } b.print(ps) } 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.writeString(" (") ps.writeString(c.Suffix) ps.writeByte(')') } 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:") first := true for _, a := range ei.Args { if !first { ps.writeString(", ") } ps.print(a) first = false } 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) } // 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 } } PK ! ���T �T demangle.gonu �[��� // 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], 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 // 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 := 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)) 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} } 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> func (st *state) name() AST { if len(st.str) < 1 { st.fail("expected name") } switch st.str[0] { case 'N': return st.nestedName() case 'Z': return st.localName() case 'U': a, isCast := st.unqualifiedName() if isCast { st.setTemplate(a, nil) } return a case 'S': if len(st.str) < 2 { st.advance(1) st.fail("expected substitution index") } var a AST isCast := false subst := false if st.str[1] == 't' { st.advance(2) a, isCast = st.unqualifiedName() a = &Qualified{Scope: &Name{Name: "std"}, Name: a, LocalName: false} } else { a = st.substitution(false) subst = true } if len(st.str) > 0 && st.str[0] == 'I' { // This can only happen if we saw // <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 default: a, isCast := st.unqualifiedName() if len(st.str) > 0 && st.str[0] == 'I' { st.subs.add(a) args := st.templateArgs() tmpl := &Template{Name: a, Args: args} if isCast { st.setTemplate(a, tmpl) st.clearTemplateArgs(args) isCast = false } a = tmpl } if isCast { st.setTemplate(a, nil) } return a } } // nestedName parses: // // <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E func (st *state) nestedName() AST { st.checkChar('N') 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 } // 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 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 == 'D' && len(st.str) > 1 && st.str[1] == 'C') { un, isUnCast := st.unqualifiedName() next = un 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) 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() 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") } } 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() (r AST, isCast bool) { if len(st.str) < 1 { st.fail("expected unqualified name") } var a AST isCast = false c := st.str[0] if isDigit(c) { a = st.sourceName() } else if isLower(c) { a, _ = st.operatorName(false) if _, ok := a.(*Cast); ok { isCast = true } if op, ok := a.(*Operator); ok && op.Name == `operator"" ` { n := st.sourceName() a = &Unary{Op: op, Expr: n, Suffix: false, SizeofType: false} } } else 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 len(st.str) > 0 && st.str[0] == 'B' { a = st.taggedName(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 } // 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 } // The operators map maps the mangled operator names to information // about them. var operators = map[string]operator{ "aN": {"&=", 2}, "aS": {"=", 2}, "aa": {"&&", 2}, "ad": {"&", 1}, "an": {"&", 2}, "at": {"alignof ", 1}, "aw": {"co_await ", 1}, "az": {"alignof ", 1}, "cc": {"const_cast", 2}, "cl": {"()", 2}, // cp is not in the ABI but is used by clang "when the call // would use ADL except for being parenthesized." "cp": {"()", 2}, "cm": {",", 2}, "co": {"~", 1}, "dV": {"/=", 2}, "dX": {"[...]=", 3}, "da": {"delete[] ", 1}, "dc": {"dynamic_cast", 2}, "de": {"*", 1}, "di": {"=", 2}, "dl": {"delete ", 1}, "ds": {".*", 2}, "dt": {".", 2}, "dv": {"/", 2}, "dx": {"]=", 2}, "eO": {"^=", 2}, "eo": {"^", 2}, "eq": {"==", 2}, "fl": {"...", 2}, "fr": {"...", 2}, "fL": {"...", 3}, "fR": {"...", 3}, "ge": {">=", 2}, "gs": {"::", 1}, "gt": {">", 2}, "ix": {"[]", 2}, "lS": {"<<=", 2}, "le": {"<=", 2}, "li": {`operator"" `, 1}, "ls": {"<<", 2}, "lt": {"<", 2}, "mI": {"-=", 2}, "mL": {"*=", 2}, "mi": {"-", 2}, "ml": {"*", 2}, "mm": {"--", 1}, "na": {"new[]", 3}, "ne": {"!=", 2}, "ng": {"-", 1}, "nt": {"!", 1}, "nw": {"new", 3}, "nx": {"noexcept", 1}, "oR": {"|=", 2}, "oo": {"||", 2}, "or": {"|", 2}, "pL": {"+=", 2}, "pl": {"+", 2}, "pm": {"->*", 2}, "pp": {"++", 1}, "ps": {"+", 1}, "pt": {"->", 2}, "qu": {"?", 3}, "rM": {"%=", 2}, "rS": {">>=", 2}, "rc": {"reinterpret_cast", 2}, "rm": {"%", 2}, "rs": {">>", 2}, "sP": {"sizeof...", 1}, "sZ": {"sizeof...", 1}, "sc": {"static_cast", 2}, "ss": {"<=>", 2}, "st": {"sizeof ", 1}, "sz": {"sizeof ", 1}, "tr": {"throw", 0}, "tw": {"throw ", 1}, } // 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}, 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> func (st *state) localName() AST { st.checkChar('Z') fn := st.encoding(true, forLocalName) if len(st.str) == 0 || st.str[0] != 'E' { st.fail("expected E after local name") } st.advance(1) if len(st.str) > 0 && st.str[0] == 's' { st.advance(1) var n AST = &Name{Name: "string literal"} n = st.discriminator(n) return &Qualified{Scope: fn, Name: n, LocalName: true} } else { num := -1 if len(st.str) > 0 && st.str[0] == 'd' { // Default argument scope. st.advance(1) num = st.compactNumber() } n := st.name() n = st.discriminator(n) if num >= 0 { n = &DefaultArg{Num: num, Arg: n} } return &Qualified{Scope: fn, Name: n, LocalName: true} } } // Parse a Java resource special-name. func (st *state) javaResource() AST { off := st.off ln := st.number() if ln <= 1 { st.failEarlier("java resource length less than 1", st.off-off) } if len(st.str) == 0 || st.str[0] != '_' { st.fail("expected _ after number") } st.advance(1) ln-- if len(st.str) < ln { st.fail("not enough characters for java resource length") } str := st.str[:ln] final := "" st.advance(ln) for i := 0; i < len(str); i++ { if str[i] != '$' { final += string(str[i]) } else { if len(str) <= i+1 { st.failEarlier("java resource escape at end of string", 1) } i++ r, ok := map[byte]string{ 'S': "/", '_': ".", '$': "$", }[str[i]] if !ok { st.failEarlier("unrecognized java resource escape", ln-i-1) } final += r } } return &Special{Prefix: "java resource ", Val: &Name{Name: final}} } // 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> 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() 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() 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() case 'F': ret = st.functionType() case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': ret = st.name() case 'A': ret = st.arrayType(isCast) case 'M': ret = st.pointerToMemberType(isCast) case 'T': 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 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 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() 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() []AST { var ret []AST for { if len(st.str) < 1 { break } if st.str[0] == 'E' || st.str[0] == '.' { break } if (st.str[0] == 'R' || st.str[0] == 'O') && len(st.str) > 1 && st.str[1] == 'E' { // This is a function ref-qualifier. break } ptype := st.demangleType(false) ret = append(ret, ptype) } // There should always be at least one type. A function that // takes no arguments will have a single parameter type // "void". if len(ret) == 0 { st.fail("expected at least one type in type list") } // Omit a single parameter type void. if len(ret) == 1 { if bt, ok := ret[0].(*BuiltinType); ok && bt.Name == "void" { ret = nil } } return ret } // 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) 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 bool) AST { if len(st.str) > 0 && st.str[0] == 'J' { hasReturnType = true st.advance(1) } var returnType AST if hasReturnType { returnType = st.demangleType(false) } types := st.parmlist() return &FunctionType{ Return: returnType, Args: types, 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 st.checkChar('T') level := 0 if len(st.str) > 0 && st.str[0] == 'L' { st.advance(1) level = st.compactNumber() } n := st.compactNumber() 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) { var seen []AST a.Traverse(func(a AST) bool { switch a := a.(type) { case *TemplateParam: if a.Template != nil { if tmpl != nil { st.fail("duplicate template parameters") } return false } if tmpl == nil { st.fail("cast template parameter not in scope of template") } if a.Index >= len(tmpl.Args) { st.fail(fmt.Sprintf("cast template index out of range (%d >= %d)", a.Index, len(tmpl.Args))) } a.Template = tmpl return false case *Closure: // There are no template params in closure types. // https://gcc.gnu.org/PR78252. return false default: for _, v := range seen { if v == a { return false } } seen = append(seen, a) return true } }) } // clearTemplateArgs gives an error for any unset Template field in // args. This handles erroneous cases where a cast operator with a // forward referenced template is in the scope of another cast // operator. func (st *state) clearTemplateArgs(args []AST) { for _, a := range args { st.setTemplate(a, nil) } } // 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 = append(ret, arg) } st.advance(1) return ret } // templateArg parses: // // <template-arg> ::= <type> // ::= X <expression> E // ::= <expr-primary> func (st *state) templateArg() AST { if len(st.str) == 0 { st.fail("missing template argument") } switch st.str[0] { case 'X': st.advance(1) expr := st.expression() if len(st.str) == 0 || st.str[0] != 'E' { st.fail("missing end of expression") } st.advance(1) return expr case 'L': return st.exprPrimary() case 'I', 'J': args := st.templateArgs() return &ArgumentPack{Args: args} default: return st.demangleType(false) } } // exprList parses a sequence of expressions up to a terminating character. func (st *state) exprList(stop byte) AST { if len(st.str) > 0 && st.str[0] == stop { st.advance(1) return &ExprList{Exprs: nil} } var exprs []AST for { e := st.expression() exprs = append(exprs, e) if len(st.str) > 0 && st.str[0] == stop { st.advance(1) break } } return &ExprList{Exprs: exprs} } // 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() 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() 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() args = append(args, arg) } return &Binary{ Op: &Operator{Name: "()"}, Left: name, Right: &ExprList{Exprs: args}, } } 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() } else { left = st.expression() } if code == "cl" || code == "cp" { right = st.exprList('E') } else if code == "dt" || code == "pt" { 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} } st.subs.add(s) } if s == nil { st.fail("missing scope in unresolved name") } st.advance(1) n := st.baseUnresolvedName() return &Qualified{Scope: s, Name: n, LocalName: false} } } else { 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 } // 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) } types := st.parmlist() st.lambdaTemplateLevel = oldLambdaTemplateLevel if template != nil { st.templates = st.templates[:len(st.templates)-1] } 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, Types: types, Num: num} } // templateParamDecl parses: // // <template-param-decl> ::= Ty # 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 '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 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 template != nil { st.templates = st.templates[:len(st.templates)-1] } tp := &TemplateTemplateParam{ Name: name, Params: params, } 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 } // 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.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} } var seen []AST 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 } for _, v := range seen { if v == a { return true } } seen = append(seen, a) return false } if c := ret.Copy(copy, skip); c != nil { return c } return ret } else { 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 { var seen []AST skip := func(a AST) bool { for _, v := range seen { if v == a { return true } } seen = append(seen, a) return false } if r := a.Copy(simplifyOne, skip); r != nil { return r } return a } // simplifyOne simplifies a single AST. It returns nil if there is // nothing to do. func simplifyOne(a AST) AST { switch a := a.(type) { case *TemplateParam: if a.Template != nil && a.Index < len(a.Template.Args) { return a.Template.Args[a.Index] } case *MethodWithQualifiers: if m, ok := a.Method.(*MethodWithQualifiers); ok { ref := a.RefQualifier if ref == "" { ref = m.RefQualifier } else if m.RefQualifier != "" { if ref == "&" || m.RefQualifier == "&" { ref = "&" } } return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: ref} } if t, ok := a.Method.(*TypeWithQualifiers); ok { return &MethodWithQualifiers{Method: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers), RefQualifier: a.RefQualifier} } case *TypeWithQualifiers: if ft, ok := a.Base.(*FunctionType); ok { return &MethodWithQualifiers{Method: ft, Qualifiers: a.Qualifiers, RefQualifier: ""} } if t, ok := a.Base.(*TypeWithQualifiers); ok { return &TypeWithQualifiers{Base: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers)} } if m, ok := a.Base.(*MethodWithQualifiers); ok { return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: m.RefQualifier} } case *ReferenceType: if rt, ok := a.Base.(*ReferenceType); ok { return rt } if rrt, ok := a.Base.(*RvalueReferenceType); ok { return &ReferenceType{Base: rrt.Base} } case *RvalueReferenceType: if rrt, ok := a.Base.(*RvalueReferenceType); ok { return rrt } if rt, ok := a.Base.(*ReferenceType); ok { return rt } case *ArrayType: // Qualifiers on the element of an array type // go on the whole array type. if q, ok := a.Element.(*TypeWithQualifiers); ok { return &TypeWithQualifiers{ Base: &ArrayType{Dimension: a.Dimension, Element: q.Base}, Qualifiers: q.Qualifiers, } } case *PackExpansion: // Expand the pack and replace it with a list of // expressions. if a.Pack != nil { exprs := make([]AST, len(a.Pack.Args)) for i, arg := range a.Pack.Args { copy := func(sub AST) AST { // Replace the ArgumentPack // with a specific argument. if sub == a.Pack { return arg } // Copy everything else. return nil } var seen []AST skip := func(sub AST) bool { // Don't traverse into another // pack expansion. if _, ok := sub.(*PackExpansion); ok { return true } for _, v := range seen { if v == sub { return true } } seen = append(seen, sub) return false } b := a.Base.Copy(copy, skip) if b == nil { b = a.Base } exprs[i] = simplify(b) } return &ExprList{Exprs: exprs} } } return nil } // findArgumentPack walks the AST looking for the argument pack for a // pack expansion. We find it via a template parameter. func (st *state) findArgumentPack(a AST) *ArgumentPack { var seen []AST var ret *ArgumentPack a.Traverse(func(a AST) bool { if ret != nil { return false } switch a := a.(type) { case *TemplateParam: if a.Template == nil || a.Index >= len(a.Template.Args) { return true } if pack, ok := a.Template.Args[a.Index].(*ArgumentPack); ok { ret = pack return false } case *PackExpansion, *Closure, *Name: return false case *TaggedName, *Operator, *BuiltinType, *FunctionParam: return false case *UnnamedType, *FixedType, *DefaultArg: return false } for _, v := range seen { if v == a { return false } } seen = append(seen, a) return true }) return ret } PK ! K��f� � SECURITY.mdnu �[��� # Security Policy ## Supported Versions Security updates are applied only to the latest release. ## Reporting a Vulnerability If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. Please disclose it at [security advisory](https://github.com/ianlancetaylor/demangle/security/advisories/new). This project is maintained by volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. PK ! {�a�� � LICENSEnu �[��� Copyright (c) 2015 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK ! ���;N N .gitignorenu �[��� *.o *.a *.so ._* .nfs.* a.out *~ *.orig *.rej *.exe .*.swp core demangle.test PK ! �̟�'] '] rust.gonu �[��� // 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 } PK ! �k k README.mdnu �[��� PK ! 8��:L L � ast.gonu �[��� PK ! ���T �T �L demangle.gonu �[��� PK ! K��f� � � SECURITY.mdnu �[��� PK ! {�a�� � � LICENSEnu �[��� PK ! ���;N N � .gitignorenu �[��� PK ! �̟�'] '] u� rust.gonu �[��� PK � �
| ver. 1.1 | |
.
| PHP 8.4.18 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0.01 |
proxy
|
phpinfo
|
ÐаÑтройка