diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-06-28 09:59:11 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-06-28 09:59:11 +0200 |
| commit | 7b4e273e539d93682dc87e9a7c7d1f79a4cd1b77 (patch) | |
| tree | c12ada52827e6b5e8e6620b880a35bb8d2322ebe /vendor/github.com/ianlancetaylor | |
| parent | c3e23de010943fd4b18e0dc95ca5b31267fabe6d (diff) | |
vendor: add github.com/ianlancetaylor/demangle
Add the new package required by pkg/report/fuchsia.go.
Also update everything else because there does not seem
to be a way to add a package without a wholesale update.
Diffstat (limited to 'vendor/github.com/ianlancetaylor')
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/.gitignore | 13 | ||||
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/LICENSE | 27 | ||||
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/README.md | 3 | ||||
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/ast.go | 2879 | ||||
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/c++filt.go | 144 | ||||
| -rw-r--r-- | vendor/github.com/ianlancetaylor/demangle/demangle.go | 2501 |
6 files changed, 5567 insertions, 0 deletions
diff --git a/vendor/github.com/ianlancetaylor/demangle/.gitignore b/vendor/github.com/ianlancetaylor/demangle/.gitignore new file mode 100644 index 000000000..4a8b38f46 --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/.gitignore @@ -0,0 +1,13 @@ +*.o +*.a +*.so +._* +.nfs.* +a.out +*~ +*.orig +*.rej +*.exe +.*.swp +core +demangle.test diff --git a/vendor/github.com/ianlancetaylor/demangle/LICENSE b/vendor/github.com/ianlancetaylor/demangle/LICENSE new file mode 100644 index 000000000..d29b37261 --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/LICENSE @@ -0,0 +1,27 @@ +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. diff --git a/vendor/github.com/ianlancetaylor/demangle/README.md b/vendor/github.com/ianlancetaylor/demangle/README.md new file mode 100644 index 000000000..ef3f94a63 --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/README.md @@ -0,0 +1,3 @@ +# github.com/ianlancetaylor/demangle + +A Go package that can be used to demangle C++ symbol names. diff --git a/vendor/github.com/ianlancetaylor/demangle/ast.go b/vendor/github.com/ianlancetaylor/demangle/ast.go new file mode 100644 index 000000000..0ad5354f5 --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/ast.go @@ -0,0 +1,2879 @@ +// 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 ( + "bytes" + "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. +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. + // Otherwise the AST returned by copy is used in a copy of the full AST. + // 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 + for _, o := range options { + switch o { + case NoTemplateParams: + tparams = false + } + } + + ps := printState{tparams: tparams} + a.print(&ps) + return ps.buf.String() +} + +// The printState type holds information needed to print an AST. +type printState struct { + tparams bool // whether to print template parameters + + buf bytes.Buffer + 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) { + 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) +} + +// Qualifiers is an ordered list of type qualifiers. +type Qualifiers []string + +// TypeWithQualifiers is a type with standard qualifiers. +type TypeWithQualifiers struct { + Base AST + Qualifiers Qualifiers +} + +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.writeString(strings.Join(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.writeString(strings.Join(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) + if base == nil { + return fn(twq) + } + twq = &TypeWithQualifiers{Base: base, Qualifiers: twq.Qualifiers} + 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: Qualifiers: %s\n%s", indent, "", field, + twq.Qualifiers, twq.Base.goString(indent+2, "Base: ")) +} + +// MethodWithQualifiers is a method with qualifiers. +type MethodWithQualifiers struct { + Method AST + Qualifiers Qualifiers + 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 len(mwq.Qualifiers) > 0 { + ps.writeByte(' ') + ps.writeString(strings.Join(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 len(mwq.Qualifiers) > 0 { + ps.writeByte(' ') + ps.writeString(strings.Join(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) + if method == nil { + return fn(mwq) + } + mwq = &MethodWithQualifiers{Method: method, Qualifiers: mwq.Qualifiers, 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 len(mwq.Qualifiers) > 0 { + q += fmt.Sprintf(" Qualifiers: %v", mwq.Qualifiers) + } + if mwq.RefQualifier != "" { + if q != "" { + q += ";" + } + q += " 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) { + ps.writeString(bt.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) { + 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. The Return field may be nil for +// cases where the return type is not part of the mangled name. +type FunctionType struct { + Return AST + Args []AST +} + +func (ft *FunctionType) print(ps *printState) { + if ft.Return != 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(ft.Return) + 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('(') + 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} + 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 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:\n%s\n%s", indent, "", field, 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 { + 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: ")) +} + +// 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) { + ps.writeString(" __vector(") + ps.print(vt.Dimension) + ps.writeByte(')') +} + +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: ")) +} + +// Decltype is the decltype operator. +type Decltype struct { + Expr AST +} + +func (dt *Decltype) print(ps *printState) { + ps.writeString("decltype (") + 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 +} + +func (c *Constructor) print(ps *printState) { + ps.print(c.Name) +} + +func (c *Constructor) Traverse(fn func(AST) bool) { + if fn(c) { + c.Name.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) + if name == nil { + return fn(c) + } + c = &Constructor{Name: name} + 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 { + return fmt.Sprintf("%*s%sConstructor:\n%s", indent, "", field, c.Name.goString(indent+2, "Name: ")) +} + +// 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 { + 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) { + 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) +} + +// 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, *FunctionParam: + 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) { + expr := u.Expr + + // Don't print the argument list when taking the address of a + // function. + if op, ok := u.Op.(*Operator); ok && 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, ok := u.Op.(*Operator); ok { + ps.writeString(op.Name) + } else if c, ok := u.Op.(*Cast); ok { + ps.writeByte('(') + ps.print(c.To) + ps.writeByte(')') + } else { + ps.print(u.Op) + } + + if !u.Suffix { + if op, ok := u.Op.(*Operator); ok && 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 { + 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: ")) +} + +// 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 + } + + // 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 + + // A function call in an expression should not print the types + // of the arguments. + if op != nil && op.Name == "()" { + if ty, ok := b.Left.(*Typed); ok { + left = ty.Name + } + } + + parenthesize(ps, left) + + if op != nil && op.Name == "[]" { + ps.writeByte('[') + ps.print(b.Right) + ps.writeByte(']') + return + } + + if op != nil { + if op.Name != "()" { + ps.writeString(op.Name) + } + } else { + ps.print(b.Op) + } + + 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) { + parenthesize(ps, t.First) + 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 { + ps.writeString(op.Name) + } else { + ps.print(f.Op) + } + } + + if f.Arg2 == nil { + if f.Left { + ps.writeString("(...") + printOp() + parenthesize(ps, f.Arg1) + ps.writeString(")") + } else { + ps.writeString("(") + parenthesize(ps, f.Arg1) + printOp() + ps.writeString("...)") + } + } else { + ps.writeString("(") + parenthesize(ps, f.Arg1) + printOp() + ps.writeString("...") + printOp() + parenthesize(ps, 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: ")) + } +} + +// 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) { + // Op doesn't really matter for printing--we always print "new". + ps.writeString("new ") + 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 { + 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) +} + +// 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) { + 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 { + Types []AST + Num int +} + +func (cl *Closure) print(ps *printState) { + ps.writeString("{lambda(") + for i, t := range cl.Types { + if i > 0 { + ps.writeString(", ") + } + ps.print(t) + } + ps.writeString(fmt.Sprintf(")#%d}", cl.Num+1)) +} + +func (cl *Closure) Traverse(fn func(AST) bool) { + if fn(cl) { + 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 + } + types := make([]AST, len(cl.Types)) + changed := false + 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{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 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", indent, "", field, cl.Num, types) +} + +// UnnamedType is an unnamed type, that just has an index. +type UnnamedType struct { + Num int +} + +func (ut *UnnamedType) print(ps *printState) { + 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) + 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) { + ps.writeString(s.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: ")) +} + +// 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: + return len(a.Args) == 0 + case *ExprList: + return len(a.Exprs) == 0 + case *PackExpansion: + return a.Pack != nil && ps.isEmpty(a.Base) + default: + return false + } +} diff --git a/vendor/github.com/ianlancetaylor/demangle/c++filt.go b/vendor/github.com/ianlancetaylor/demangle/c++filt.go new file mode 100644 index 000000000..7ba817c9f --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/c++filt.go @@ -0,0 +1,144 @@ +// 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. + +// +build ignore + +// This is a program that works like the GNU c++filt program. +// It's here for testing purposes and as an example. + +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "os" + "strings" + "unicode" + + "github.com/ianlancetaylor/demangle" +) + +func flagUsage() { + usage(os.Stderr, 2) +} + +func usage(w io.Writer, status int) { + fmt.Fprintf(w, "Usage: %s [options] [mangled names]\n", os.Args[0]) + flag.CommandLine.SetOutput(w) + flag.PrintDefaults() + fmt.Fprintln(w, `Demangled names are displayed to stdout +If a name cannot be demangled it is just echoed to stdout. +If no names are provided on the command line, stdin is read.`) + os.Exit(status) +} + +var stripUnderscore = flag.Bool("_", false, "Ignore first leading underscore") +var noParams = flag.Bool("p", false, "Do not display function argument types") +var noVerbose = flag.Bool("i", false, "Do not show implementation details (if any)") +var help = flag.Bool("h", false, "Display help information") +var debug = flag.Bool("d", false, "Display debugging information for strings on command line") + +// Unimplemented c++filt flags: +// -n (opposite of -_) +// -t (demangle types) +// -s (set demangling style) +// -V (print version information) + +// Characters considered to be part of a symbol. +const symbolChars = "_$." + +func main() { + flag.Usage = func() { usage(os.Stderr, 1) } + flag.Parse() + + if *help { + usage(os.Stdout, 0) + } + + out := bufio.NewWriter(os.Stdout) + + if flag.NArg() > 0 { + for _, f := range flag.Args() { + if *debug { + a, err := demangle.ToAST(f, options()...) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", f, err) + } else { + fmt.Fprintf(out, "%#v\n", a) + } + } else { + doDemangle(out, f) + } + out.WriteByte('\n') + } + if err := out.Flush(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + return + } + + scanner := bufio.NewScanner(bufio.NewReader(os.Stdin)) + for scanner.Scan() { + line := scanner.Text() + start := -1 + for i, c := range line { + if unicode.IsLetter(c) || unicode.IsNumber(c) || strings.ContainsRune(symbolChars, c) { + if start < 0 { + start = i + } + } else { + if start >= 0 { + doDemangle(out, line[start:i]) + } + out.WriteRune(c) + start = -1 + } + } + if start >= 0 { + doDemangle(out, line[start:]) + start = -1 + } + out.WriteByte('\n') + if err := out.Flush(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + } +} + +// Demangle a string just as the GNU c++filt program does. +func doDemangle(out *bufio.Writer, name string) { + skip := 0 + if name[0] == '.' || name[0] == '$' { + skip++ + } + if *stripUnderscore && name[skip] == '_' { + skip++ + } + result := demangle.Filter(name[skip:], options()...) + if result == name[skip:] { + out.WriteString(name) + } else { + if name[0] == '.' { + out.WriteByte('.') + } + out.WriteString(result) + } +} + +// options returns the demangling options to use based on the command +// line flags. +func options() []demangle.Option { + var options []demangle.Option + if *noParams { + options = append(options, demangle.NoParams) + } + if !*noVerbose { + options = append(options, demangle.Verbose) + } + return options +} diff --git a/vendor/github.com/ianlancetaylor/demangle/demangle.go b/vendor/github.com/ianlancetaylor/demangle/demangle.go new file mode 100644 index 000000000..c46744017 --- /dev/null +++ b/vendor/github.com/ianlancetaylor/demangle/demangle.go @@ -0,0 +1,2501 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package demangle defines functions that demangle GCC/LLVM C++ symbol names. +// This package recognizes names that were mangled according to the C++ ABI +// defined at http://codesourcery.com/cxx-abi/. +package demangle + +import ( + "errors" + "fmt" + "strings" +) + +// ErrNotMangledName is returned by CheckedDemangle if the string does +// not appear to be a C++ symbol name. +var ErrNotMangledName = errors.New("not a C++ mangled name") + +// Option is the type of demangler options. +type Option int + +const ( + // The NoParams option disables demangling of function parameters. + NoParams Option = iota + + // The NoTemplateParams option disables demangling of template parameters. + NoTemplateParams + + // The NoClones option disables inclusion of clone suffixes. + // NoParams implies NoClones. + NoClones + + // The Verbose option turns on more verbose demangling. + Verbose +) + +// Filter demangles a C++ symbol name, returning the human-readable C++ name. +// If any error occurs during demangling, the input string is returned. +func Filter(name string, options ...Option) string { + ret, err := ToString(name, options...) + if err != nil { + return name + } + return ret +} + +// ToString demangles a C++ symbol name, returning human-readable C++ +// name or an error. +// If the name does not appear to be a C++ symbol name at all, the +// error will be ErrNotMangledName. +func ToString(name string, options ...Option) (string, error) { + a, err := ToAST(name, options...) + if err != nil { + return "", err + } + return ASTToString(a, options...), nil +} + +// ToAST demangles a C++ symbol name into an abstract syntax tree +// representing the symbol. +// If the NoParams option is passed, and the name has a function type, +// the parameter types are not demangled. +// If the name does not appear to be a C++ symbol name at all, the +// error will be ErrNotMangledName. +func ToAST(name string, options ...Option) (AST, error) { + if strings.HasPrefix(name, "_Z") { + a, err := doDemangle(name[2:], options...) + return a, adjustErr(err, 2) + } + + const prefix = "_GLOBAL_" + if strings.HasPrefix(name, prefix) { + // The standard demangler ignores NoParams for global + // constructors. We are compatible. + i := 0 + for i < len(options) { + if options[i] == NoParams { + options = append(options[:i], options[i+1:]...) + } else { + i++ + } + } + a, err := globalCDtorName(name[len(prefix):], options...) + return a, adjustErr(err, len(prefix)) + } + + return nil, ErrNotMangledName +} + +// globalCDtorName demangles a global constructor/destructor symbol name. +// The parameter is the string following the "_GLOBAL_" prefix. +func globalCDtorName(name string, options ...Option) (AST, error) { + if len(name) < 4 { + return nil, ErrNotMangledName + } + switch name[0] { + case '.', '_', '$': + default: + return nil, ErrNotMangledName + } + + var ctor bool + switch name[1] { + case 'I': + ctor = true + case 'D': + ctor = false + default: + return nil, ErrNotMangledName + } + + if name[2] != '_' { + return nil, ErrNotMangledName + } + + if !strings.HasPrefix(name[3:], "_Z") { + return &GlobalCDtor{Ctor: ctor, Key: &Name{Name: name}}, nil + } else { + a, err := doDemangle(name[5:], options...) + if err != nil { + return nil, adjustErr(err, 5) + } + return &GlobalCDtor{Ctor: ctor, Key: a}, nil + } +} + +// The doDemangle function is the entry point into the demangler proper. +func doDemangle(name string, options ...Option) (ret AST, err error) { + // When the demangling routines encounter an error, they panic + // with a value of type demangleErr. + defer func() { + if r := recover(); r != nil { + if de, ok := r.(demangleErr); ok { + ret = nil + err = de + return + } + panic(r) + } + }() + + params := true + clones := true + verbose := false + for _, o := range options { + switch o { + case NoParams: + params = false + clones = false + case NoTemplateParams: + // This is a valid option but only affect printing of the AST. + case NoClones: + clones = false + case Verbose: + verbose = true + default: + return nil, fmt.Errorf("unrecognized demangler option %v", o) + } + } + + st := &state{str: name, verbose: verbose} + a := st.encoding(params) + + // Accept a clone suffix. + if clones { + for len(st.str) > 1 && st.str[0] == '.' && (isLower(st.str[1]) || st.str[1] == '_' || isDigit(st.str[1])) { + a = st.cloneSuffix(a) + } + } + + if clones && len(st.str) > 0 { + st.fail("unparsed characters at end of mangled name") + } + + return a, nil +} + +// A state holds the current state of demangling a string. +type state struct { + str string // remainder of string to demangle + verbose bool // whether to use verbose demangling + off int // offset of str within original string + subs substitutions // substitutions + templates []*Template // templates being processed +} + +// copy returns a copy of the current state. +func (st *state) copy() *state { + n := new(state) + *n = *st + return n +} + +// fail panics with demangleErr, to be caught in doDemangle. +func (st *state) fail(err string) { + panic(demangleErr{err: err, off: st.off}) +} + +// failEarlier is like fail, but decrements the offset to indicate +// that the point of failure occurred earlier in the string. +func (st *state) failEarlier(err string, dec int) { + if st.off < dec { + panic("internal error") + } + panic(demangleErr{err: err, off: st.off - dec}) +} + +// advance advances the current string offset. +func (st *state) advance(add int) { + if len(st.str) < add { + panic("internal error") + } + st.str = st.str[add:] + st.off += add +} + +// checkChar requires that the next character in the string be c, and +// advances past it. +func (st *state) checkChar(c byte) { + if len(st.str) == 0 || st.str[0] != c { + panic("internal error") + } + st.advance(1) +} + +// A demangleErr is an error at a specific offset in the mangled +// string. +type demangleErr struct { + err string + off int +} + +// Error implements the builtin error interface for demangleErr. +func (de demangleErr) Error() string { + return fmt.Sprintf("%s at %d", de.err, de.off) +} + +// adjustErr adjusts the position of err, if it is a demangleErr, +// and returns err. +func adjustErr(err error, adj int) error { + if err == nil { + return nil + } + if de, ok := err.(demangleErr); ok { + de.off += adj + return de + } + return err +} + +// encoding ::= <(function) name> <bare-function-type> +// <(data) name> +// <special-name> +func (st *state) encoding(params bool) AST { + if len(st.str) < 1 { + st.fail("expected encoding") + } + + if st.str[0] == 'G' || st.str[0] == 'T' { + return st.specialName() + } + + a := st.name() + a = simplify(a) + + if !params { + // Don't demangle the parameters. + + // Strip CV-qualifiers, as they apply to the 'this' + // parameter, and are not output by the standard + // demangler without parameters. + if mwq, ok := a.(*MethodWithQualifiers); ok { + a = mwq.Method + } + + // If this is a local name, there may be CV-qualifiers + // on the name that really apply to the top level, and + // therefore must be discarded when discarding + // parameters. This can happen when parsing a class + // that is local to a function. + if q, ok := a.(*Qualified); ok && q.LocalName { + p := &q.Name + if da, ok := (*p).(*DefaultArg); ok { + p = &da.Arg + } + if mwq, ok := (*p).(*MethodWithQualifiers); ok { + *p = mwq.Method + } + } + + return a + } + + if len(st.str) == 0 || st.str[0] == 'E' { + // There are no parameters--this is a data symbol, not + // a function symbol. + return a + } + + check := a + mwq, _ := check.(*MethodWithQualifiers) + if mwq != nil { + check = mwq.Method + } + template, _ := check.(*Template) + if template != nil { + st.templates = append(st.templates, template) + } + + ft := st.bareFunctionType(hasReturnType(a)) + + if template != nil { + st.templates = st.templates[:len(st.templates)-1] + } + + ft = simplify(ft) + + // Any top-level qualifiers belong to the function type. + if mwq != nil { + a = mwq.Method + mwq.Method = ft + ft = mwq + } + if q, ok := a.(*Qualified); ok && q.LocalName { + p := &q.Name + if da, ok := (*p).(*DefaultArg); ok { + p = &da.Arg + } + if mwq, ok := (*p).(*MethodWithQualifiers); ok { + *p = mwq.Method + mwq.Method = ft + ft = mwq + } + } + + return &Typed{Name: a, Type: ft} +} + +// hasReturnType returns whether the mangled form of a will have a +// return type. +func hasReturnType(a AST) bool { + switch a := a.(type) { + case *Template: + return !isCDtorConversion(a.Name) + case *TypeWithQualifiers: + return hasReturnType(a.Base) + case *MethodWithQualifiers: + return hasReturnType(a.Method) + default: + return false + } +} + +// isCDtorConversion returns when an AST is a constructor, a +// destructor, or a conversion operator. +func isCDtorConversion(a AST) bool { + switch a := a.(type) { + case *Qualified: + return isCDtorConversion(a.Name) + case *Constructor, *Destructor, *Cast: + return true + default: + return false + } +} + +// <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> ::= <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 + } +} + +// <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 len(q) > 0 || r != "" { + a = &MethodWithQualifiers{Method: a, Qualifiers: q, RefQualifier: r} + } + if len(st.str) == 0 || st.str[0] != 'E' { + st.fail("expected E after nested name") + } + st.advance(1) + return a +} + +// <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 + } + } + } + + isCast := false + for { + if len(st.str) == 0 { + st.fail("expected prefix") + } + var next AST + + c := st.str[0] + if isDigit(c) || isLower(c) || c == 'U' || c == 'L' { + un, isUnCast := st.unqualifiedName() + next = un + if isUnCast { + isCast = true + } + } else { + switch st.str[0] { + case 'C': + if len(st.str) < 2 { + st.fail("expected constructor type") + } + if last == nil { + st.fail("constructor before name is seen") + } + st.advance(2) + next = &Constructor{Name: getLast(last)} + case 'D': + if len(st.str) > 1 && (st.str[1] == 'T' || st.str[1] == 't') { + next = st.demangleType(false) + } else { + if len(st.str) < 2 { + st.fail("expected destructor type") + } + if last == nil { + st.fail("destructor before name is seen") + } + st.advance(2) + next = &Destructor{Name: getLast(last)} + } + case 'S': + next = st.substitution(true) + case 'I': + if a == nil { + st.fail("unexpected template arguments") + } + var args []AST + args = st.templateArgs() + tmpl := &Template{Name: a, Args: args} + if isCast { + st.setTemplate(a, tmpl) + st.clearTemplateArgs(args) + isCast = false + } + a = nil + next = tmpl + case 'T': + next = st.templateParam() + case 'E': + if a == nil { + st.fail("expected prefix") + } + if isCast { + st.setTemplate(a, nil) + } + return a + case 'M': + if a == nil { + st.fail("unexpected lambda initializer") + } + // This is the initializer scope for a + // lambda. We don't need to record + // it. The normal code will treat the + // variable has a type scope, which + // gives appropriate output. + st.advance(1) + continue + default: + st.fail("unrecognized letter in prefix") + } + } + last = next + if a == nil { + a = next + } else { + a = &Qualified{Scope: a, Name: next, LocalName: false} + } + + if c != 'S' && (len(st.str) == 0 || st.str[0] != 'E') { + st.subs.add(a) + } + } +} + +// <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 { + switch c { + case 'C', 'D': + st.fail("constructor/destructor not in nested name") + case 'L': + st.advance(1) + a = st.sourceName() + a = st.discriminator(a) + case 'U': + if len(st.str) < 2 { + st.advance(1) + st.fail("expected closure or unnamed type") + } + c := st.str[1] + switch c { + case 'l': + a = st.closureTypeName() + case 't': + a = st.unnamedTypeName() + default: + st.advance(1) + st.fail("expected closure or unnamed type") + } + default: + st.fail("expected unqualified name") + } + } + + if len(st.str) > 0 && st.str[0] == 'B' { + a = st.taggedName(a) + } + + return a, isCast +} + +// <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 ::= [n] <(non-negative decimal integer)> +func (st *state) number() int { + neg := false + if len(st.str) > 0 && st.str[0] == 'n' { + neg = true + st.advance(1) + } + if len(st.str) == 0 || !isDigit(st.str[0]) { + st.fail("missing number") + } + val := 0 + for len(st.str) > 0 && isDigit(st.str[0]) { + // Number picked to ensure we can't overflow with 32-bit int. + // Any very large number here is bogus. + if val >= 0x80000000/10-10 { + st.fail("numeric overflow") + } + val = val*10 + int(st.str[0]-'0') + st.advance(1) + } + if neg { + val = -val + } + return val +} + +// An operator is the demangled name, and the number of arguments it +// takes in an expression. +type operator struct { + name string + args int +} + +// The operators map maps the mangled operator names to information +// about them. +var operators = map[string]operator{ + "aN": {"&=", 2}, + "aS": {"=", 2}, + "aa": {"&&", 2}, + "ad": {"&", 1}, + "an": {"&", 2}, + "at": {"alignof ", 1}, + "az": {"alignof ", 1}, + "cc": {"const_cast", 2}, + "cl": {"()", 2}, + "cm": {",", 2}, + "co": {"~", 1}, + "dV": {"/=", 2}, + "da": {"delete[] ", 1}, + "dc": {"dynamic_cast", 2}, + "de": {"*", 1}, + "dl": {"delete ", 1}, + "ds": {".*", 2}, + "dt": {".", 2}, + "dv": {"/", 2}, + "eO": {"^=", 2}, + "eo": {"^", 2}, + "eq": {"==", 2}, + "fl": {"...", 2}, + "fr": {"...", 2}, + "fL": {"...", 3}, + "fR": {"...", 3}, + "ge": {">=", 2}, + "gs": {"::", 1}, + "gt": {">", 2}, + "ix": {"[]", 2}, + "lS": {"<<=", 2}, + "le": {"<=", 2}, + "li": {`operator"" `, 1}, + "ls": {"<<", 2}, + "lt": {"<", 2}, + "mI": {"-=", 2}, + "mL": {"*=", 2}, + "mi": {"-", 2}, + "ml": {"*", 2}, + "mm": {"--", 1}, + "na": {"new[]", 3}, + "ne": {"!=", 2}, + "ng": {"-", 1}, + "nt": {"!", 1}, + "nw": {"new", 3}, + "oR": {"|=", 2}, + "oo": {"||", 2}, + "or": {"|", 2}, + "pL": {"+=", 2}, + "pl": {"+", 2}, + "pm": {"->*", 2}, + "pp": {"++", 1}, + "ps": {"+", 1}, + "pt": {"->", 2}, + "qu": {"?", 3}, + "rM": {"%=", 2}, + "rS": {">>=", 2}, + "rc": {"reinterpret_cast", 2}, + "rm": {"%", 2}, + "rs": {">>", 2}, + "sc": {"static_cast", 2}, + "st": {"sizeof ", 1}, + "sz": {"sizeof ", 1}, + "tr": {"throw", 0}, + "tw": {"throw ", 1}, +} + +// operator_name ::= many different two character encodings. +// ::= cv <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") + } +} + +// <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) + 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() + } + var n AST = 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}} +} + +// <special-name> ::= TV <type> +// ::= TT <type> +// ::= TI <type> +// ::= TS <type> +// ::= GV <(object) name> +// ::= T <call-offset> <(base) encoding> +// ::= Tc <call-offset> <call-offset> <(base) encoding> +// Also 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 'h': + st.callOffset('h') + v := st.encoding(true) + return &Special{Prefix: "non-virtual thunk to ", Val: v} + case 'v': + st.callOffset('v') + v := st.encoding(true) + return &Special{Prefix: "virtual thunk to ", Val: v} + case 'c': + st.callOffset(0) + st.callOffset(0) + v := st.encoding(true) + return &Special{Prefix: "covariant return thunk to ", Val: v} + case 'C': + derived := st.demangleType(false) + off := st.off + offset := st.number() + if offset < 0 { + st.failEarlier("expected positive offset", st.off-off) + } + if len(st.str) == 0 || st.str[0] != '_' { + st.fail("expected _ after number") + } + st.advance(1) + base := st.demangleType(false) + return &Special2{Prefix: "construction vtable for ", Val1: base, Middle: "-in-", Val2: derived} + case 'F': + t := st.demangleType(false) + return &Special{Prefix: "typeinfo fn for ", Val: t} + case 'J': + t := st.demangleType(false) + return &Special{Prefix: "java Class for ", Val: t} + case 'H': + n := st.name() + return &Special{Prefix: "TLS init function for ", Val: n} + case 'W': + n := st.name() + return &Special{Prefix: "TLS wrapper function for ", Val: n} + default: + st.fail("unrecognized special T name code") + panic("not reached") + } + } else { + st.checkChar('G') + if len(st.str) == 0 { + st.fail("expected special name code") + } + c := st.str[0] + st.advance(1) + switch c { + case 'V': + n := st.name() + return &Special{Prefix: "guard variable for ", Val: n} + case 'R': + n := st.name() + i := st.number() + return &Special{Prefix: fmt.Sprintf("reference temporary #%d for ", i), Val: n} + case 'A': + v := st.encoding(true) + 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) + 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") + } + } +} + +// <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': "...", +} + +// <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 len(q) > 0 { + if len(st.str) == 0 { + st.fail("expected type") + } + + // CV-qualifiers before a function type apply to + // 'this', so avoid adding the unqualified function + // type to the substitution list. + if st.str[0] == 'F' { + addSubst = false + } + } + + var ret AST + + // Use correct substitution for a template parameter. + var sub AST + + if btype, ok := builtinTypes[st.str[0]]; ok { + ret = &BuiltinType{Name: btype} + st.advance(1) + if len(q) > 0 { + ret = &TypeWithQualifiers{Base: ret, Qualifiers: q} + st.subs.add(ret) + } + return ret + } + c := st.str[0] + switch c { + case 'u': + st.advance(1) + ret = st.sourceName() + case 'F': + ret = st.functionType() + case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + ret = st.name() + case 'A': + ret = st.arrayType(isCast) + case 'M': + ret = st.pointerToMemberType(isCast) + case 'T': + ret = st.templateParam() + if len(st.str) > 0 && st.str[0] == 'I' { + // See the function comment to explain this. + if !isCast { + st.subs.add(ret) + args := st.templateArgs() + ret = &Template{Name: ret, Args: args} + } else { + ret = st.demangleCastTemplateArgs(ret, true) + } + } + case 'S': + // If this is a special substitution, then it + // is the start of <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 'f': + ret = &BuiltinType{Name: "decimal32"} + case 'd': + ret = &BuiltinType{Name: "decimal64"} + case 'e': + ret = &BuiltinType{Name: "decimal128"} + case 'h': + ret = &BuiltinType{Name: "half"} + case 's': + ret = &BuiltinType{Name: "char16_t"} + case 'i': + ret = &BuiltinType{Name: "char32_t"} + case 'n': + ret = &BuiltinType{Name: "decltype(nullptr)"} + + case 'F': + accum := false + if len(st.str) > 0 && isDigit(st.str[0]) { + accum = true + // We don't care about the bits. + _ = st.number() + } + base := st.demangleType(isCast) + if len(st.str) > 0 && isDigit(st.str[0]) { + // We don't care about the bits. + st.number() + } + sat := false + if len(st.str) > 0 { + if st.str[0] == 's' { + sat = true + } + st.advance(1) + } + ret = &FixedType{Base: base, Accum: accum, Sat: sat} + + case 'v': + ret = st.vectorType(isCast) + addSubst = true + + default: + st.fail("unrecognized D code in type") + } + + default: + st.fail("unrecognized type code") + } + + if addSubst { + if sub != nil { + st.subs.add(sub) + } else { + st.subs.add(ret) + } + } + + if len(q) > 0 { + if _, ok := ret.(*FunctionType); ok { + ret = &MethodWithQualifiers{Method: ret, Qualifiers: q, RefQualifier: ""} + } else if mwq, ok := ret.(*MethodWithQualifiers); ok { + // Merge adjacent qualifiers. This case + // happens with a function with a trailing + // ref-qualifier. + mwq.Qualifiers = mergeQualifiers(q, mwq.Qualifiers) + } else { + // Merge adjacent qualifiers. This case + // happens with multi-dimensional array types. + if qsub, ok := ret.(*TypeWithQualifiers); ok { + q = mergeQualifiers(q, qsub.Qualifiers) + ret = qsub.Base + } + ret = &TypeWithQualifiers{Base: ret, Qualifiers: q} + } + st.subs.add(ret) + } + + return ret +} + +// demangleCastTemplateArgs is for a rather hideous parse. When we +// see a template-param followed by a template-args, we need to decide +// whether we have a template-param or a template-template-param. +// Normally it is template-template-param, meaning that we pick up the +// template arguments here. But, if we are parsing the type for a +// cast operator, then the only way this can be template-template-param +// is if there is another set of template-args immediately after this +// set. That would look like this: +// +// <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 qualifer lists into one. +func mergeQualifiers(q1, q2 Qualifiers) Qualifiers { + m := make(map[string]bool) + for _, qual := range q1 { + m[qual] = true + } + for _, qual := range q2 { + if !m[qual] { + q1 = append(q1, qual) + m[qual] = true + } + } + return q1 +} + +// qualifiers maps from the character used in the mangled name to the +// string to print. +var qualifiers = map[byte]string{ + 'r': "restrict", + 'V': "volatile", + 'K': "const", +} + +// <CV-qualifiers> ::= [r] [V] [K] +func (st *state) cvQualifiers() Qualifiers { + var q Qualifiers + for len(st.str) > 0 { + if qv, ok := qualifiers[st.str[0]]; ok { + q = append([]string{qv}, q...) + st.advance(1) + } else if len(st.str) > 1 && st.str[:2] == "Dx" { + q = append([]string{"transaction_safe"}, q...) + st.advance(2) + } else { + break + } + } + return q +} + +// <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 "" +} + +// <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 +} + +// <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 +} + +// <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} +} + +// <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 +} + +// <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} +} + +// <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} +} + +// <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 +} + +// <template-param> ::= T_ +// ::= T <(parameter-2 non-negative) number> _ +// +// When a template parameter is a substitution candidate, any +// reference to that substitution refers to the template parameter +// with the same index in the currently active template, not to +// whatever the template parameter would be expanded to here. We sort +// this out in substitution and simplify. +func (st *state) templateParam() AST { + if len(st.templates) == 0 { + st.fail("template parameter not in scope of template") + } + off := st.off + + st.checkChar('T') + n := st.compactNumber() + + template := st.templates[len(st.templates)-1] + + if template == nil { + // We are parsing a cast operator. If the cast is + // itself a template, then this is a forward + // reference. Fill it in later. + return &TemplateParam{Index: n, Template: nil} + } + + if n >= len(template.Args) { + st.failEarlier(fmt.Sprintf("template index out of range (%d >= %d)", n, len(template.Args)), st.off-off) + } + + return &TemplateParam{Index: n, Template: template} +} + +// setTemplate sets the Template field of any TemplateParam's in a. +// This handles the forward referencing template parameters found in +// cast operators. +func (st *state) setTemplate(a AST, tmpl *Template) { + var seen []AST + a.Traverse(func(a AST) bool { + switch a := a.(type) { + case *TemplateParam: + if a.Template != nil { + if tmpl != nil { + st.fail("duplicate template parameters") + } + return false + } + if tmpl == nil { + st.fail("cast template parameter not in scope of template") + } + if a.Index >= len(tmpl.Args) { + st.fail(fmt.Sprintf("cast template index out of range (%d >= %d)", a.Index, len(tmpl.Args))) + } + a.Template = tmpl + return false + default: + for _, v := range seen { + if v == a { + return false + } + } + seen = append(seen, a) + return true + } + }) +} + +// clearTemplateArgs gives an error for any unset Template field in +// args. This handles erroneous cases where a cast operator with a +// forward referenced template is in the scope of another cast +// operator. +func (st *state) clearTemplateArgs(args []AST) { + for _, a := range args { + st.setTemplate(a, nil) + } +} + +// <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 +} + +// <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> ::= <(unary) operator-name> <expression> +// ::= <(binary) operator-name> <expression> <expression> +// ::= <(trinary) operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E +// ::= st <type> +// ::= <template-param> +// ::= sr <type> <unqualified-name> +// ::= sr <type> <unqualified-name> <template-args> +// ::= <expr-primary> +func (st *state) expression() AST { + if len(st.str) == 0 { + st.fail("expected expression") + } + if st.str[0] == 'L' { + return st.exprPrimary() + } else if st.str[0] == 'T' { + return st.templateParam() + } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' { + st.advance(2) + if len(st.str) == 0 { + st.fail("expected unresolved type") + } + switch st.str[0] { + case 'T', 'D', 'S': + t := st.demangleType(false) + n := st.baseUnresolvedName() + n = &Qualified{Scope: t, Name: n, LocalName: false} + if len(st.str) > 0 && st.str[0] == 'I' { + args := st.templateArgs() + n = &Template{Name: n, Args: args} + } + return n + default: + var s AST + if st.str[0] == 'N' { + st.advance(1) + s = st.demangleType(false) + } + for len(st.str) == 0 || st.str[0] != 'E' { + // GCC does not seem to follow the ABI here. + // It can emit type/name without an 'E'. + if s != nil && len(st.str) > 0 && !isDigit(st.str[0]) { + if q, ok := s.(*Qualified); ok { + a := q.Scope + if t, ok := a.(*Template); ok { + st.subs.add(t.Name) + st.subs.add(t) + } else { + st.subs.add(a) + } + return s + } + } + n := st.sourceName() + if len(st.str) > 0 && st.str[0] == 'I' { + st.subs.add(n) + args := st.templateArgs() + n = &Template{Name: n, Args: args} + } + if s == nil { + s = n + } else { + s = &Qualified{Scope: s, Name: n, LocalName: false} + } + st.subs.add(s) + } + if s == nil { + st.fail("missing scope in unresolved name") + } + st.advance(1) + n := st.baseUnresolvedName() + return &Qualified{Scope: s, Name: n, LocalName: false} + } + } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'p' { + st.advance(2) + e := st.expression() + pack := st.findArgumentPack(e) + return &PackExpansion{Base: e, Pack: pack} + } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'Z' { + st.advance(2) + off := st.off + e := st.expression() + ap := st.findArgumentPack(e) + if ap == nil { + st.failEarlier("missing argument pack", st.off-off) + } + return &SizeofPack{Pack: ap} + } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'P' { + st.advance(2) + var args []AST + for len(st.str) == 0 || st.str[0] != 'E' { + arg := st.templateArg() + args = append(args, arg) + } + st.advance(1) + return &SizeofArgs{Args: args} + } else if st.str[0] == 'f' && len(st.str) > 1 && st.str[1] == 'p' { + st.advance(2) + if len(st.str) > 0 && st.str[0] == 'T' { + st.advance(1) + return &FunctionParam{Index: 0} + } else { + index := st.compactNumber() + return &FunctionParam{Index: index + 1} + } + } else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') { + if st.str[0] == 'o' { + // Skip operator function ID. + st.advance(2) + } + n, _ := st.unqualifiedName() + if len(st.str) > 0 && st.str[0] == 'I' { + args := st.templateArgs() + n = &Template{Name: n, Args: args} + } + return n + } else if (st.str[0] == 'i' || st.str[0] == 't') && len(st.str) > 1 && st.str[1] == 'l' { + // Brace-enclosed initializer list. + c := st.str[0] + st.advance(2) + var t AST + if c == 't' { + t = st.demangleType(false) + } + exprs := st.exprList('E') + return &InitializerList{Type: t, Exprs: exprs} + } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 't' { + o, _ := st.operatorName(true) + t := st.demangleType(false) + return &Unary{Op: o, Expr: t, Suffix: false, SizeofType: true} + } else { + if len(st.str) < 2 { + st.fail("missing operator code") + } + code := st.str[:2] + o, args := st.operatorName(true) + switch args { + case 0: + return &Nullary{Op: o} + + case 1: + suffix := false + if code == "pp" || code == "mm" { + if len(st.str) > 0 && st.str[0] == '_' { + st.advance(1) + } else { + suffix = true + } + } + var operand AST + if _, ok := o.(*Cast); ok && len(st.str) > 0 && st.str[0] == '_' { + st.advance(1) + operand = st.exprList('E') + } else { + operand = st.expression() + } + return &Unary{Op: o, Expr: operand, Suffix: suffix, SizeofType: false} + + case 2: + var left, right AST + if code == "sc" || code == "dc" || code == "cc" || code == "rc" { + left = st.demangleType(false) + } else if code[0] == 'f' { + left, _ = st.operatorName(true) + right = st.expression() + return &Fold{Left: code[1] == 'l', Op: left, Arg1: right, Arg2: nil} + } else { + left = st.expression() + } + if code == "cl" { + right = st.exprList('E') + } else if code == "dt" || code == "pt" { + right, _ = st.unqualifiedName() + if len(st.str) > 0 && st.str[0] == 'I' { + args := st.templateArgs() + right = &Template{Name: right, Args: args} + } + } else { + right = st.expression() + } + return &Binary{Op: o, Left: left, Right: right} + + case 3: + if code[0] == 'n' { + if code[1] != 'w' && code[1] != 'a' { + panic("internal error") + } + place := st.exprList('_') + if place.(*ExprList).Exprs == nil { + place = nil + } + t := st.demangleType(false) + var ini AST + if len(st.str) > 0 && st.str[0] == 'E' { + st.advance(1) + } else if len(st.str) > 1 && st.str[0] == 'p' && st.str[1] == 'i' { + // Parenthesized initializer. + st.advance(2) + ini = st.exprList('E') + } else if len(st.str) > 1 && st.str[0] == 'i' && st.str[1] == 'l' { + // Initializer list. + ini = st.expression() + } else { + st.fail("unrecognized new initializer") + } + return &New{Op: o, Place: place, Type: t, Init: ini} + } else if code[0] == 'f' { + first, _ := st.operatorName(true) + second := st.expression() + third := st.expression() + return &Fold{Left: code[1] == 'L', Op: first, Arg1: second, Arg2: third} + } else { + first := st.expression() + second := st.expression() + third := st.expression() + return &Trinary{Op: o, First: first, Second: second, Third: third} + } + + default: + st.fail(fmt.Sprintf("unsupported number of operator arguments: %d", args)) + panic("not reached") + } + } +} + +// <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 +} + +// <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) + } else { + t := st.demangleType(false) + + neg := false + if len(st.str) > 0 && st.str[0] == 'n' { + neg = true + st.advance(1) + } + 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> ::= _ <(non-negative) number> +func (st *state) discriminator(a AST) AST { + if len(st.str) == 0 || st.str[0] != '_' { + return a + } + off := st.off + st.advance(1) + d := st.number() + if d < 0 { + st.failEarlier("invalid negative discriminator", st.off-off) + } + // We don't currently print out the discriminator, so we don't + // save it. + return a +} + +// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ +func (st *state) closureTypeName() AST { + st.checkChar('U') + st.checkChar('l') + types := st.parmlist() + if len(st.str) == 0 || st.str[0] != 'E' { + st.fail("expected E after closure type name") + } + st.advance(1) + num := st.compactNumber() + ret := &Closure{Types: types, Num: num} + st.subs.add(ret) + return ret +} + +// <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]) || st.str[1] == '_') { + i += 2 + for len(st.str) > i && (isLower(st.str[i]) || st.str[i] == '_') { + i++ + } + } + for len(st.str) > i+1 && st.str[i] == '.' && isDigit(st.str[i+1]) { + i += 2 + for len(st.str) > i && isDigit(st.str[i]) { + i++ + } + } + suffix := st.str[:i] + st.advance(i) + return &Clone{Base: a, Suffix: suffix} +} + +// substitutions is the list of substitution candidates that may +// appear later in the string. +type substitutions []AST + +// add adds a new substitution candidate. +func (subs *substitutions) add(a AST) { + *subs = append(*subs, a) +} + +// subAST maps standard substitution codes to the corresponding AST. +var subAST = map[byte]AST{ + 't': &Name{Name: "std"}, + 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}}, + 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}}, + 's': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "string"}}, + 'i': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "istream"}}, + 'o': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "ostream"}}, + 'd': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "iostream"}}, +} + +// verboseAST maps standard substitution codes to the long form of the +// corresponding AST. We use this when the Verbose option is used, to +// match the standard demangler. +var verboseAST = map[byte]AST{ + 't': &Name{Name: "std"}, + 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}}, + 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}}, + + // std::basic_string<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> ::= 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] + st.advance(1) + dec := 1 + if c == '_' || isDigit(c) || isUpper(c) { + id := 0 + if c != '_' { + for c != '_' { + // Don't overflow a 32-bit int. + if id >= 0x80000000/36-36 { + st.fail("substitution index overflow") + } + if isDigit(c) { + id = id*36 + int(c-'0') + } else if isUpper(c) { + id = id*36 + int(c-'A') + 10 + } else { + st.fail("invalid character in substitution index") + } + + if len(st.str) == 0 { + st.fail("missing end to substitution index") + } + c = st.str[0] + st.advance(1) + dec++ + } + id++ + } + + if id >= len(st.subs) { + st.failEarlier(fmt.Sprintf("substitution index out of range (%d >= %d)", id, len(st.subs)), dec) + } + + ret := st.subs[id] + + // We need to update any references to template + // parameters to refer to the currently active + // template. + copy := func(a AST) AST { + tp, ok := a.(*TemplateParam) + if !ok { + return nil + } + if len(st.templates) == 0 { + st.failEarlier("substituted template parameter not in scope of template", dec) + } + template := st.templates[len(st.templates)-1] + if template == nil { + // This template parameter is within + // the scope of a cast operator. + return &TemplateParam{Index: tp.Index, Template: nil} + } + + if tp.Index >= len(template.Args) { + st.failEarlier(fmt.Sprintf("substituted template index out of range (%d >= %d)", tp.Index, len(template.Args)), dec) + } + + return &TemplateParam{Index: tp.Index, Template: template} + } + var seen []AST + skip := func(a AST) bool { + if _, ok := a.(*Typed); ok { + return true + } + for _, v := range seen { + if v == a { + return true + } + } + seen = append(seen, a) + return false + } + if c := ret.Copy(copy, skip); c != nil { + return c + } + + return ret + } else { + m := subAST + if st.verbose { + m = verboseAST + } + // For compatibility with the standard demangler, use + // a longer name for a constructor or destructor. + if forPrefix && len(st.str) > 0 && (st.str[0] == 'C' || st.str[0] == 'D') { + m = verboseAST + } + a, ok := m[c] + if !ok { + st.failEarlier("unrecognized substitution code", 1) + } + + if len(st.str) > 0 && st.str[0] == 'B' { + a = st.taggedName(a) + } + + return a + } +} + +// isDigit returns whetner c is a digit for demangling purposes. +func isDigit(c byte) bool { + return c >= '0' && c <= '9' +} + +// isUpper returns whether c is an upper case letter for demangling purposes. +func isUpper(c byte) bool { + return c >= 'A' && c <= 'Z' +} + +// isLower returns whether c is a lower case letter for demangling purposes. +func isLower(c byte) bool { + return c >= 'a' && c <= 'z' +} + +// simplify replaces template parameters with their expansions, and +// merges qualifiers. +func simplify(a AST) AST { + var seen []AST + skip := func(a AST) bool { + for _, v := range seen { + if v == a { + return true + } + } + seen = append(seen, a) + return false + } + if r := a.Copy(simplifyOne, skip); r != nil { + return r + } + return a +} + +// simplifyOne simplifies a single AST. It returns nil if there is +// nothing to do. +func simplifyOne(a AST) AST { + switch a := a.(type) { + case *TemplateParam: + if a.Template != nil && a.Index < len(a.Template.Args) { + return a.Template.Args[a.Index] + } + case *MethodWithQualifiers: + if m, ok := a.Method.(*MethodWithQualifiers); ok { + ref := a.RefQualifier + if ref == "" { + ref = m.RefQualifier + } else if m.RefQualifier != "" { + if ref == "&" || m.RefQualifier == "&" { + ref = "&" + } + } + return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: ref} + } + if t, ok := a.Method.(*TypeWithQualifiers); ok { + return &MethodWithQualifiers{Method: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers), RefQualifier: a.RefQualifier} + } + case *TypeWithQualifiers: + if ft, ok := a.Base.(*FunctionType); ok { + return &MethodWithQualifiers{Method: ft, Qualifiers: a.Qualifiers, RefQualifier: ""} + } + if t, ok := a.Base.(*TypeWithQualifiers); ok { + return &TypeWithQualifiers{Base: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers)} + } + if m, ok := a.Base.(*MethodWithQualifiers); ok { + return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: m.RefQualifier} + } + case *ReferenceType: + if rt, ok := a.Base.(*ReferenceType); ok { + return rt + } + if rrt, ok := a.Base.(*RvalueReferenceType); ok { + return &ReferenceType{Base: rrt.Base} + } + case *RvalueReferenceType: + if rrt, ok := a.Base.(*RvalueReferenceType); ok { + return rrt + } + if rt, ok := a.Base.(*ReferenceType); ok { + return rt + } + case *ArrayType: + // Qualifiers on the element of an array type + // go on the whole array type. + if q, ok := a.Element.(*TypeWithQualifiers); ok { + return &TypeWithQualifiers{ + Base: &ArrayType{Dimension: a.Dimension, Element: q.Base}, + Qualifiers: q.Qualifiers, + } + } + case *PackExpansion: + // Expand the pack and replace it with a list of + // expressions. + if a.Pack != nil { + exprs := make([]AST, len(a.Pack.Args)) + for i, arg := range a.Pack.Args { + copy := func(sub AST) AST { + // Replace the ArgumentPack + // with a specific argument. + if sub == a.Pack { + return arg + } + // Copy everything else. + return nil + } + + var seen []AST + skip := func(sub AST) bool { + // Don't traverse into another + // pack expansion. + if _, ok := sub.(*PackExpansion); ok { + return true + } + for _, v := range seen { + if v == sub { + return true + } + } + seen = append(seen, sub) + return false + } + + b := a.Base.Copy(copy, skip) + if b == nil { + b = a.Base + } + exprs[i] = simplify(b) + } + return &ExprList{Exprs: exprs} + } + } + return nil +} + +// findArgumentPack walks the AST looking for the argument pack for a +// pack expansion. We find it via a template parameter. +func (st *state) findArgumentPack(a AST) *ArgumentPack { + var seen []AST + var ret *ArgumentPack + a.Traverse(func(a AST) bool { + if ret != nil { + return false + } + switch a := a.(type) { + case *TemplateParam: + if a.Template == nil || a.Index >= len(a.Template.Args) { + return true + } + if pack, ok := a.Template.Args[a.Index].(*ArgumentPack); ok { + ret = pack + return false + } + case *PackExpansion, *Closure, *Name: + return false + case *TaggedName, *Operator, *BuiltinType, *FunctionParam: + return false + case *UnnamedType, *FixedType, *DefaultArg: + return false + } + for _, v := range seen { + if v == a { + return false + } + } + seen = append(seen, a) + return true + }) + return ret +} |
