1166 lines
23 KiB
Go
1166 lines
23 KiB
Go
// Copyright 2021 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package demangle
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/bits"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// rustToString demangles a Rust symbol.
|
|
func rustToString(name string, options []Option) (ret string, err error) {
|
|
if !strings.HasPrefix(name, "_R") {
|
|
return "", ErrNotMangledName
|
|
}
|
|
|
|
// When the demangling routines encounter an error, they panic
|
|
// with a value of type demangleErr.
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
if de, ok := r.(demangleErr); ok {
|
|
ret = ""
|
|
err = de
|
|
return
|
|
}
|
|
panic(r)
|
|
}
|
|
}()
|
|
|
|
suffix := ""
|
|
dot := strings.Index(name, ".")
|
|
if dot >= 0 {
|
|
suffix = name[dot:]
|
|
name = name[:dot]
|
|
}
|
|
|
|
name = name[2:]
|
|
rst := &rustState{orig: name, str: name}
|
|
|
|
for _, o := range options {
|
|
if o == NoTemplateParams {
|
|
rst.noGenericArgs = true
|
|
} else if isMaxLength(o) {
|
|
rst.max = maxLength(o)
|
|
}
|
|
}
|
|
|
|
rst.symbolName()
|
|
|
|
if len(rst.str) > 0 {
|
|
rst.fail("unparsed characters at end of mangled name")
|
|
}
|
|
|
|
if suffix != "" {
|
|
llvmStyle := false
|
|
for _, o := range options {
|
|
if o == LLVMStyle {
|
|
llvmStyle = true
|
|
break
|
|
}
|
|
}
|
|
if llvmStyle {
|
|
rst.skip = false
|
|
rst.writeString(" (")
|
|
rst.writeString(suffix)
|
|
rst.writeByte(')')
|
|
}
|
|
}
|
|
|
|
s := rst.buf.String()
|
|
if rst.max > 0 && len(s) > rst.max {
|
|
s = s[:rst.max]
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// A rustState holds the current state of demangling a Rust string.
|
|
type rustState struct {
|
|
orig string // the original string being demangled
|
|
str string // remainder of string to demangle
|
|
off int // offset of str within original string
|
|
buf strings.Builder // demangled string being built
|
|
skip bool // don't print, just skip
|
|
lifetimes int64 // number of bound lifetimes
|
|
last byte // last byte written to buffer
|
|
noGenericArgs bool // don't demangle generic arguments
|
|
max int // maximum output length
|
|
}
|
|
|
|
// fail panics with demangleErr, to be caught in rustToString.
|
|
func (rst *rustState) fail(err string) {
|
|
panic(demangleErr{err: err, off: rst.off})
|
|
}
|
|
|
|
// advance advances the current string offset.
|
|
func (rst *rustState) advance(add int) {
|
|
if len(rst.str) < add {
|
|
panic("internal error")
|
|
}
|
|
rst.str = rst.str[add:]
|
|
rst.off += add
|
|
}
|
|
|
|
// checkChar requires that the next character in the string be c,
|
|
// and advances past it.
|
|
func (rst *rustState) checkChar(c byte) {
|
|
if len(rst.str) == 0 || rst.str[0] != c {
|
|
rst.fail("expected " + string(c))
|
|
}
|
|
rst.advance(1)
|
|
}
|
|
|
|
// writeByte writes a byte to the buffer.
|
|
func (rst *rustState) writeByte(c byte) {
|
|
if rst.skip {
|
|
return
|
|
}
|
|
if rst.max > 0 && rst.buf.Len() > rst.max {
|
|
rst.skip = true
|
|
return
|
|
}
|
|
rst.last = c
|
|
rst.buf.WriteByte(c)
|
|
}
|
|
|
|
// writeString writes a string to the buffer.
|
|
func (rst *rustState) writeString(s string) {
|
|
if rst.skip {
|
|
return
|
|
}
|
|
if rst.max > 0 && rst.buf.Len() > rst.max {
|
|
rst.skip = true
|
|
return
|
|
}
|
|
if len(s) > 0 {
|
|
rst.last = s[len(s)-1]
|
|
rst.buf.WriteString(s)
|
|
}
|
|
}
|
|
|
|
// symbolName parses:
|
|
//
|
|
// <symbol-name> = "_R" [<decimal-number>] <path> [<instantiating-crate>]
|
|
// <instantiating-crate> = <path>
|
|
//
|
|
// We've already skipped the "_R".
|
|
func (rst *rustState) symbolName() {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected symbol-name")
|
|
}
|
|
|
|
if isDigit(rst.str[0]) {
|
|
rst.fail("unsupported Rust encoding version")
|
|
}
|
|
|
|
rst.path(true)
|
|
|
|
if len(rst.str) > 0 {
|
|
rst.skip = true
|
|
rst.path(false)
|
|
}
|
|
}
|
|
|
|
// path parses:
|
|
//
|
|
// <path> = "C" <identifier> // crate root
|
|
// | "M" <impl-path> <type> // <T> (inherent impl)
|
|
// | "X" <impl-path> <type> <path> // <T as Trait> (trait impl)
|
|
// | "Y" <type> <path> // <T as Trait> (trait definition)
|
|
// | "N" <namespace> <path> <identifier> // ...::ident (nested path)
|
|
// | "I" <path> {<generic-arg>} "E" // ...<T, U> (generic args)
|
|
// | <backref>
|
|
// <namespace> = "C" // closure
|
|
// | "S" // shim
|
|
// | <A-Z> // other special namespaces
|
|
// | <a-z> // internal namespaces
|
|
//
|
|
// needsSeparator is true if we need to write out :: for a generic;
|
|
// it is passed as false if we are in the middle of a type.
|
|
func (rst *rustState) path(needsSeparator bool) {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected path")
|
|
}
|
|
switch c := rst.str[0]; c {
|
|
case 'C':
|
|
rst.advance(1)
|
|
_, ident := rst.identifier()
|
|
rst.writeString(ident)
|
|
case 'M', 'X':
|
|
rst.advance(1)
|
|
rst.implPath()
|
|
rst.writeByte('<')
|
|
rst.demangleType()
|
|
if c == 'X' {
|
|
rst.writeString(" as ")
|
|
rst.path(false)
|
|
}
|
|
rst.writeByte('>')
|
|
case 'Y':
|
|
rst.advance(1)
|
|
rst.writeByte('<')
|
|
rst.demangleType()
|
|
rst.writeString(" as ")
|
|
rst.path(false)
|
|
rst.writeByte('>')
|
|
case 'N':
|
|
rst.advance(1)
|
|
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected namespace")
|
|
}
|
|
ns := rst.str[0]
|
|
switch {
|
|
case ns >= 'a' && ns <= 'z':
|
|
case ns >= 'A' && ns <= 'Z':
|
|
default:
|
|
rst.fail("invalid namespace character")
|
|
}
|
|
rst.advance(1)
|
|
|
|
rst.path(needsSeparator)
|
|
|
|
dis, ident := rst.identifier()
|
|
|
|
if ns >= 'A' && ns <= 'Z' {
|
|
rst.writeString("::{")
|
|
switch ns {
|
|
case 'C':
|
|
rst.writeString("closure")
|
|
case 'S':
|
|
rst.writeString("shim")
|
|
default:
|
|
rst.writeByte(ns)
|
|
}
|
|
if len(ident) > 0 {
|
|
rst.writeByte(':')
|
|
rst.writeString(ident)
|
|
}
|
|
if !rst.skip {
|
|
fmt.Fprintf(&rst.buf, "#%d}", dis)
|
|
rst.last = '}'
|
|
}
|
|
} else {
|
|
rst.writeString("::")
|
|
rst.writeString(ident)
|
|
}
|
|
case 'I':
|
|
rst.advance(1)
|
|
rst.path(needsSeparator)
|
|
if needsSeparator {
|
|
rst.writeString("::")
|
|
}
|
|
rst.writeByte('<')
|
|
rst.genericArgs()
|
|
rst.writeByte('>')
|
|
rst.checkChar('E')
|
|
case 'B':
|
|
rst.backref(func() { rst.path(needsSeparator) })
|
|
default:
|
|
rst.fail("unrecognized letter in path")
|
|
}
|
|
}
|
|
|
|
// implPath parses:
|
|
//
|
|
// <impl-path> = [<disambiguator>] <path>
|
|
func (rst *rustState) implPath() {
|
|
// This path is not part of the demangled string.
|
|
hold := rst.skip
|
|
rst.skip = true
|
|
defer func() {
|
|
rst.skip = hold
|
|
}()
|
|
|
|
rst.disambiguator()
|
|
rst.path(false)
|
|
}
|
|
|
|
// identifier parses:
|
|
//
|
|
// <identifier> = [<disambiguator>] <undisambiguated-identifier>
|
|
//
|
|
// It returns the disambiguator and the identifier.
|
|
func (rst *rustState) identifier() (int64, string) {
|
|
dis := rst.disambiguator()
|
|
ident, _ := rst.undisambiguatedIdentifier()
|
|
return dis, ident
|
|
}
|
|
|
|
// disambiguator parses an optional:
|
|
//
|
|
// <disambiguator> = "s" <base-62-number>
|
|
func (rst *rustState) disambiguator() int64 {
|
|
if len(rst.str) == 0 || rst.str[0] != 's' {
|
|
return 0
|
|
}
|
|
rst.advance(1)
|
|
return rst.base62Number() + 1
|
|
}
|
|
|
|
// undisambiguatedIdentifier parses:
|
|
//
|
|
// <undisambiguated-identifier> = ["u"] <decimal-number> ["_"] <bytes>
|
|
func (rst *rustState) undisambiguatedIdentifier() (id string, isPunycode bool) {
|
|
isPunycode = false
|
|
if len(rst.str) > 0 && rst.str[0] == 'u' {
|
|
rst.advance(1)
|
|
isPunycode = true
|
|
}
|
|
|
|
val := rst.decimalNumber()
|
|
|
|
if len(rst.str) > 0 && rst.str[0] == '_' {
|
|
rst.advance(1)
|
|
}
|
|
|
|
if len(rst.str) < val {
|
|
rst.fail("not enough characters for identifier")
|
|
}
|
|
id = rst.str[:val]
|
|
rst.advance(val)
|
|
|
|
for i := 0; i < len(id); i++ {
|
|
c := id[i]
|
|
switch {
|
|
case c >= '0' && c <= '9':
|
|
case c >= 'A' && c <= 'Z':
|
|
case c >= 'a' && c <= 'z':
|
|
case c == '_':
|
|
default:
|
|
rst.fail("invalid character in identifier")
|
|
}
|
|
}
|
|
|
|
if isPunycode {
|
|
id = rst.expandPunycode(id)
|
|
}
|
|
|
|
return id, isPunycode
|
|
}
|
|
|
|
// expandPunycode decodes the Rust version of punycode.
|
|
// This algorithm is taken from RFC 3492 section 6.2.
|
|
func (rst *rustState) expandPunycode(s string) string {
|
|
const (
|
|
base = 36
|
|
tmin = 1
|
|
tmax = 26
|
|
skew = 38
|
|
damp = 700
|
|
initialBias = 72
|
|
initialN = 128
|
|
)
|
|
|
|
var (
|
|
output []rune
|
|
encoding string
|
|
)
|
|
idx := strings.LastIndex(s, "_")
|
|
if idx >= 0 {
|
|
output = []rune(s[:idx])
|
|
encoding = s[idx+1:]
|
|
} else {
|
|
encoding = s
|
|
}
|
|
|
|
i := 0
|
|
n := initialN
|
|
bias := initialBias
|
|
|
|
pos := 0
|
|
for pos < len(encoding) {
|
|
oldI := i
|
|
w := 1
|
|
for k := base; ; k += base {
|
|
if pos == len(encoding) {
|
|
rst.fail("unterminated punycode")
|
|
}
|
|
|
|
var digit byte
|
|
d := encoding[pos]
|
|
pos++
|
|
switch {
|
|
case '0' <= d && d <= '9':
|
|
digit = d - '0' + 26
|
|
case 'A' <= d && d <= 'Z':
|
|
digit = d - 'A'
|
|
case 'a' <= d && d <= 'z':
|
|
digit = d - 'a'
|
|
default:
|
|
rst.fail("invalid punycode digit")
|
|
}
|
|
|
|
i += int(digit) * w
|
|
if i < 0 {
|
|
rst.fail("punycode number overflow")
|
|
}
|
|
|
|
var t int
|
|
if k <= bias {
|
|
t = tmin
|
|
} else if k > bias+tmax {
|
|
t = tmax
|
|
} else {
|
|
t = k - bias
|
|
}
|
|
|
|
if int(digit) < t {
|
|
break
|
|
}
|
|
|
|
if w >= math.MaxInt32/base {
|
|
rst.fail("punycode number overflow")
|
|
}
|
|
w *= base - t
|
|
}
|
|
|
|
delta := i - oldI
|
|
numPoints := len(output) + 1
|
|
firstTime := oldI == 0
|
|
if firstTime {
|
|
delta /= damp
|
|
} else {
|
|
delta /= 2
|
|
}
|
|
delta += delta / numPoints
|
|
k := 0
|
|
for delta > ((base-tmin)*tmax)/2 {
|
|
delta /= base - tmin
|
|
k += base
|
|
}
|
|
bias = k + ((base-tmin+1)*delta)/(delta+skew)
|
|
|
|
n += i / (len(output) + 1)
|
|
if n > utf8.MaxRune {
|
|
rst.fail("punycode rune overflow")
|
|
} else if !utf8.ValidRune(rune(n)) {
|
|
rst.fail("punycode invalid code point")
|
|
}
|
|
i %= len(output) + 1
|
|
output = append(output, 0)
|
|
copy(output[i+1:], output[i:])
|
|
output[i] = rune(n)
|
|
i++
|
|
}
|
|
|
|
return string(output)
|
|
}
|
|
|
|
// genericArgs prints a list of generic arguments, without angle brackets.
|
|
func (rst *rustState) genericArgs() {
|
|
if rst.noGenericArgs {
|
|
hold := rst.skip
|
|
rst.skip = true
|
|
defer func() {
|
|
rst.skip = hold
|
|
}()
|
|
}
|
|
|
|
first := true
|
|
for len(rst.str) > 0 && rst.str[0] != 'E' {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
rst.writeString(", ")
|
|
}
|
|
rst.genericArg()
|
|
}
|
|
}
|
|
|
|
// genericArg parses:
|
|
//
|
|
// <generic-arg> = <lifetime>
|
|
// | <type>
|
|
// | "K" <const> // forward-compat for const generics
|
|
// <lifetime> = "L" <base-62-number>
|
|
func (rst *rustState) genericArg() {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected generic-arg")
|
|
}
|
|
if rst.str[0] == 'L' {
|
|
rst.advance(1)
|
|
rst.writeLifetime(rst.base62Number())
|
|
} else if rst.str[0] == 'K' {
|
|
rst.advance(1)
|
|
rst.demangleConst()
|
|
} else {
|
|
rst.demangleType()
|
|
}
|
|
}
|
|
|
|
// binder parses an optional:
|
|
//
|
|
// <binder> = "G" <base-62-number>
|
|
func (rst *rustState) binder() {
|
|
if len(rst.str) < 1 || rst.str[0] != 'G' {
|
|
return
|
|
}
|
|
rst.advance(1)
|
|
|
|
binderLifetimes := rst.base62Number() + 1
|
|
|
|
// Every bound lifetime should be referenced later.
|
|
if binderLifetimes >= int64(len(rst.str))-rst.lifetimes {
|
|
rst.fail("binder lifetimes overflow")
|
|
}
|
|
|
|
rst.writeString("for<")
|
|
for i := int64(0); i < binderLifetimes; i++ {
|
|
if i > 0 {
|
|
rst.writeString(", ")
|
|
}
|
|
rst.lifetimes++
|
|
rst.writeLifetime(1)
|
|
}
|
|
rst.writeString("> ")
|
|
}
|
|
|
|
// demangleType parses:
|
|
//
|
|
// <type> = <basic-type>
|
|
// | <path> // named type
|
|
// | "A" <type> <const> // [T; N]
|
|
// | "S" <type> // [T]
|
|
// | "T" {<type>} "E" // (T1, T2, T3, ...)
|
|
// | "R" [<lifetime>] <type> // &T
|
|
// | "Q" [<lifetime>] <type> // &mut T
|
|
// | "P" <type> // *const T
|
|
// | "O" <type> // *mut T
|
|
// | "F" <fn-sig> // fn(...) -> ...
|
|
// | "D" <dyn-bounds> <lifetime> // dyn Trait<Assoc = X> + Send + 'a
|
|
// | <backref>
|
|
func (rst *rustState) demangleType() {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected type")
|
|
}
|
|
c := rst.str[0]
|
|
if c >= 'a' && c <= 'z' {
|
|
rst.basicType()
|
|
return
|
|
}
|
|
switch c {
|
|
case 'C', 'M', 'X', 'Y', 'N', 'I':
|
|
rst.path(false)
|
|
case 'A', 'S':
|
|
rst.advance(1)
|
|
rst.writeByte('[')
|
|
rst.demangleType()
|
|
if c == 'A' {
|
|
rst.writeString("; ")
|
|
rst.demangleConst()
|
|
}
|
|
rst.writeByte(']')
|
|
case 'T':
|
|
rst.advance(1)
|
|
rst.writeByte('(')
|
|
c := 0
|
|
for len(rst.str) > 0 && rst.str[0] != 'E' {
|
|
if c > 0 {
|
|
rst.writeString(", ")
|
|
}
|
|
c++
|
|
rst.demangleType()
|
|
}
|
|
if c == 1 {
|
|
rst.writeByte(',')
|
|
}
|
|
rst.writeByte(')')
|
|
rst.checkChar('E')
|
|
case 'R', 'Q':
|
|
rst.advance(1)
|
|
rst.writeByte('&')
|
|
if len(rst.str) > 0 && rst.str[0] == 'L' {
|
|
rst.advance(1)
|
|
if lifetime := rst.base62Number(); lifetime > 0 {
|
|
rst.writeLifetime(lifetime)
|
|
rst.writeByte(' ')
|
|
}
|
|
}
|
|
if c == 'Q' {
|
|
rst.writeString("mut ")
|
|
}
|
|
rst.demangleType()
|
|
case 'P':
|
|
rst.advance(1)
|
|
rst.writeString("*const ")
|
|
rst.demangleType()
|
|
case 'O':
|
|
rst.advance(1)
|
|
rst.writeString("*mut ")
|
|
rst.demangleType()
|
|
case 'F':
|
|
rst.advance(1)
|
|
hold := rst.lifetimes
|
|
rst.fnSig()
|
|
rst.lifetimes = hold
|
|
case 'D':
|
|
rst.advance(1)
|
|
hold := rst.lifetimes
|
|
rst.dynBounds()
|
|
rst.lifetimes = hold
|
|
if len(rst.str) == 0 || rst.str[0] != 'L' {
|
|
rst.fail("expected L")
|
|
}
|
|
rst.advance(1)
|
|
if lifetime := rst.base62Number(); lifetime > 0 {
|
|
if rst.last != ' ' {
|
|
rst.writeByte(' ')
|
|
}
|
|
rst.writeString("+ ")
|
|
rst.writeLifetime(lifetime)
|
|
}
|
|
case 'B':
|
|
rst.backref(rst.demangleType)
|
|
default:
|
|
rst.fail("unrecognized character in type")
|
|
}
|
|
}
|
|
|
|
var rustBasicTypes = map[byte]string{
|
|
'a': "i8",
|
|
'b': "bool",
|
|
'c': "char",
|
|
'd': "f64",
|
|
'e': "str",
|
|
'f': "f32",
|
|
'h': "u8",
|
|
'i': "isize",
|
|
'j': "usize",
|
|
'l': "i32",
|
|
'm': "u32",
|
|
'n': "i128",
|
|
'o': "u128",
|
|
'p': "_",
|
|
's': "i16",
|
|
't': "u16",
|
|
'u': "()",
|
|
'v': "...",
|
|
'x': "i64",
|
|
'y': "u64",
|
|
'z': "!",
|
|
}
|
|
|
|
// basicType parses:
|
|
//
|
|
// <basic-type>
|
|
func (rst *rustState) basicType() {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected basic type")
|
|
}
|
|
str, ok := rustBasicTypes[rst.str[0]]
|
|
if !ok {
|
|
rst.fail("unrecognized basic type character")
|
|
}
|
|
rst.advance(1)
|
|
rst.writeString(str)
|
|
}
|
|
|
|
// fnSig parses:
|
|
//
|
|
// <fn-sig> = [<binder>] ["U"] ["K" <abi>] {<type>} "E" <type>
|
|
// <abi> = "C"
|
|
// | <undisambiguated-identifier>
|
|
func (rst *rustState) fnSig() {
|
|
rst.binder()
|
|
if len(rst.str) > 0 && rst.str[0] == 'U' {
|
|
rst.advance(1)
|
|
rst.writeString("unsafe ")
|
|
}
|
|
if len(rst.str) > 0 && rst.str[0] == 'K' {
|
|
rst.advance(1)
|
|
if len(rst.str) > 0 && rst.str[0] == 'C' {
|
|
rst.advance(1)
|
|
rst.writeString(`extern "C" `)
|
|
} else {
|
|
rst.writeString(`extern "`)
|
|
id, isPunycode := rst.undisambiguatedIdentifier()
|
|
if isPunycode {
|
|
rst.fail("punycode used in ABI string")
|
|
}
|
|
id = strings.ReplaceAll(id, "_", "-")
|
|
rst.writeString(id)
|
|
rst.writeString(`" `)
|
|
}
|
|
}
|
|
rst.writeString("fn(")
|
|
first := true
|
|
for len(rst.str) > 0 && rst.str[0] != 'E' {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
rst.writeString(", ")
|
|
}
|
|
rst.demangleType()
|
|
}
|
|
rst.checkChar('E')
|
|
rst.writeByte(')')
|
|
if len(rst.str) > 0 && rst.str[0] == 'u' {
|
|
rst.advance(1)
|
|
} else {
|
|
rst.writeString(" -> ")
|
|
rst.demangleType()
|
|
}
|
|
}
|
|
|
|
// dynBounds parses:
|
|
//
|
|
// <dyn-bounds> = [<binder>] {<dyn-trait>} "E"
|
|
func (rst *rustState) dynBounds() {
|
|
rst.writeString("dyn ")
|
|
rst.binder()
|
|
first := true
|
|
for len(rst.str) > 0 && rst.str[0] != 'E' {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
rst.writeString(" + ")
|
|
}
|
|
rst.dynTrait()
|
|
}
|
|
rst.checkChar('E')
|
|
}
|
|
|
|
// dynTrait parses:
|
|
//
|
|
// <dyn-trait> = <path> {<dyn-trait-assoc-binding>}
|
|
// <dyn-trait-assoc-binding> = "p" <undisambiguated-identifier> <type>
|
|
func (rst *rustState) dynTrait() {
|
|
started := rst.pathStartGenerics()
|
|
for len(rst.str) > 0 && rst.str[0] == 'p' {
|
|
rst.advance(1)
|
|
if started {
|
|
rst.writeString(", ")
|
|
} else {
|
|
rst.writeByte('<')
|
|
started = true
|
|
}
|
|
id, _ := rst.undisambiguatedIdentifier()
|
|
rst.writeString(id)
|
|
rst.writeString(" = ")
|
|
rst.demangleType()
|
|
}
|
|
if started {
|
|
rst.writeByte('>')
|
|
}
|
|
}
|
|
|
|
// pathStartGenerics is like path but if it sees an I to start generic
|
|
// arguments it won't close them. It reports whether it started generics.
|
|
func (rst *rustState) pathStartGenerics() bool {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected path")
|
|
}
|
|
switch rst.str[0] {
|
|
case 'I':
|
|
rst.advance(1)
|
|
rst.path(false)
|
|
rst.writeByte('<')
|
|
rst.genericArgs()
|
|
rst.checkChar('E')
|
|
return true
|
|
case 'B':
|
|
var started bool
|
|
rst.backref(func() { started = rst.pathStartGenerics() })
|
|
return started
|
|
default:
|
|
rst.path(false)
|
|
return false
|
|
}
|
|
}
|
|
|
|
// writeLifetime writes out a lifetime binding.
|
|
func (rst *rustState) writeLifetime(lifetime int64) {
|
|
rst.writeByte('\'')
|
|
if lifetime == 0 {
|
|
rst.writeByte('_')
|
|
return
|
|
}
|
|
depth := rst.lifetimes - lifetime
|
|
if depth < 0 {
|
|
rst.fail("invalid lifetime")
|
|
} else if depth < 26 {
|
|
rst.writeByte('a' + byte(depth))
|
|
} else {
|
|
rst.writeByte('z')
|
|
if !rst.skip {
|
|
fmt.Fprintf(&rst.buf, "%d", depth-26+1)
|
|
rst.last = '0'
|
|
}
|
|
}
|
|
}
|
|
|
|
// demangleConst parses:
|
|
//
|
|
// <const> = <type> <const-data>
|
|
// | "p" // placeholder, shown as _
|
|
// | <backref>
|
|
// <const-data> = ["n"] {<hex-digit>} "_"
|
|
func (rst *rustState) demangleConst() {
|
|
if len(rst.str) < 1 {
|
|
rst.fail("expected constant")
|
|
}
|
|
|
|
if rst.str[0] == 'B' {
|
|
rst.backref(rst.demangleConst)
|
|
return
|
|
}
|
|
|
|
if rst.str[0] == 'p' {
|
|
rst.advance(1)
|
|
rst.writeByte('_')
|
|
return
|
|
}
|
|
|
|
typ := rst.str[0]
|
|
|
|
const (
|
|
invalid = iota
|
|
signedInt
|
|
unsignedInt
|
|
boolean
|
|
character
|
|
)
|
|
|
|
var kind int
|
|
switch typ {
|
|
case 'a', 's', 'l', 'x', 'n', 'i':
|
|
kind = signedInt
|
|
case 'h', 't', 'm', 'y', 'o', 'j':
|
|
kind = unsignedInt
|
|
case 'b':
|
|
kind = boolean
|
|
case 'c':
|
|
kind = character
|
|
default:
|
|
rst.fail("unrecognized constant type")
|
|
}
|
|
|
|
rst.advance(1)
|
|
|
|
if kind == signedInt && len(rst.str) > 0 && rst.str[0] == 'n' {
|
|
rst.advance(1)
|
|
rst.writeByte('-')
|
|
}
|
|
|
|
start := rst.str
|
|
digits := 0
|
|
val := uint64(0)
|
|
digitLoop:
|
|
for len(rst.str) > 0 {
|
|
c := rst.str[0]
|
|
var digit uint64
|
|
switch {
|
|
case c >= '0' && c <= '9':
|
|
digit = uint64(c - '0')
|
|
case c >= 'a' && c <= 'f':
|
|
digit = uint64(c - 'a' + 10)
|
|
case c == '_':
|
|
rst.advance(1)
|
|
break digitLoop
|
|
default:
|
|
rst.fail("expected hex digit or _")
|
|
}
|
|
rst.advance(1)
|
|
if val == 0 && digit == 0 && (len(rst.str) == 0 || rst.str[0] != '_') {
|
|
rst.fail("invalid leading 0 in constant")
|
|
}
|
|
val *= 16
|
|
val += digit
|
|
digits++
|
|
}
|
|
|
|
if digits == 0 {
|
|
rst.fail("expected constant")
|
|
}
|
|
|
|
switch kind {
|
|
case signedInt, unsignedInt:
|
|
if digits > 16 {
|
|
// Value too big, just write out the string.
|
|
rst.writeString("0x")
|
|
rst.writeString(start[:digits])
|
|
} else {
|
|
if !rst.skip {
|
|
fmt.Fprintf(&rst.buf, "%d", val)
|
|
rst.last = '0'
|
|
}
|
|
}
|
|
case boolean:
|
|
if digits > 1 {
|
|
rst.fail("boolean value too large")
|
|
} else if val == 0 {
|
|
rst.writeString("false")
|
|
} else if val == 1 {
|
|
rst.writeString("true")
|
|
} else {
|
|
rst.fail("invalid boolean value")
|
|
}
|
|
case character:
|
|
if digits > 6 {
|
|
rst.fail("character value too large")
|
|
}
|
|
rst.writeByte('\'')
|
|
if val == '\t' {
|
|
rst.writeString(`\t`)
|
|
} else if val == '\r' {
|
|
rst.writeString(`\r`)
|
|
} else if val == '\n' {
|
|
rst.writeString(`\n`)
|
|
} else if val == '\\' {
|
|
rst.writeString(`\\`)
|
|
} else if val == '\'' {
|
|
rst.writeString(`\'`)
|
|
} else if val >= ' ' && val <= '~' {
|
|
// printable ASCII character
|
|
rst.writeByte(byte(val))
|
|
} else {
|
|
if !rst.skip {
|
|
fmt.Fprintf(&rst.buf, `\u{%x}`, val)
|
|
rst.last = '}'
|
|
}
|
|
}
|
|
rst.writeByte('\'')
|
|
default:
|
|
panic("internal error")
|
|
}
|
|
}
|
|
|
|
// base62Number parses:
|
|
//
|
|
// <base-62-number> = {<0-9a-zA-Z>} "_"
|
|
func (rst *rustState) base62Number() int64 {
|
|
if len(rst.str) > 0 && rst.str[0] == '_' {
|
|
rst.advance(1)
|
|
return 0
|
|
}
|
|
val := int64(0)
|
|
for len(rst.str) > 0 {
|
|
c := rst.str[0]
|
|
rst.advance(1)
|
|
if c == '_' {
|
|
return val + 1
|
|
}
|
|
val *= 62
|
|
if c >= '0' && c <= '9' {
|
|
val += int64(c - '0')
|
|
} else if c >= 'a' && c <= 'z' {
|
|
val += int64(c - 'a' + 10)
|
|
} else if c >= 'A' && c <= 'Z' {
|
|
val += int64(c - 'A' + 36)
|
|
} else {
|
|
rst.fail("invalid digit in base 62 number")
|
|
}
|
|
}
|
|
rst.fail("expected _ after base 62 number")
|
|
return 0
|
|
}
|
|
|
|
// backref parses:
|
|
//
|
|
// <backref> = "B" <base-62-number>
|
|
func (rst *rustState) backref(demangle func()) {
|
|
backoff := rst.off
|
|
|
|
rst.checkChar('B')
|
|
idx64 := rst.base62Number()
|
|
|
|
if rst.skip {
|
|
return
|
|
}
|
|
if rst.max > 0 && rst.buf.Len() > rst.max {
|
|
return
|
|
}
|
|
|
|
idx := int(idx64)
|
|
if int64(idx) != idx64 {
|
|
rst.fail("backref index overflow")
|
|
}
|
|
if idx < 0 || idx >= backoff {
|
|
rst.fail("invalid backref index")
|
|
}
|
|
|
|
holdStr := rst.str
|
|
holdOff := rst.off
|
|
rst.str = rst.orig[idx:backoff]
|
|
rst.off = idx
|
|
defer func() {
|
|
rst.str = holdStr
|
|
rst.off = holdOff
|
|
}()
|
|
|
|
demangle()
|
|
}
|
|
|
|
func (rst *rustState) decimalNumber() int {
|
|
if len(rst.str) == 0 {
|
|
rst.fail("expected number")
|
|
}
|
|
|
|
val := 0
|
|
for len(rst.str) > 0 && isDigit(rst.str[0]) {
|
|
add := int(rst.str[0] - '0')
|
|
if val >= math.MaxInt32/10-add {
|
|
rst.fail("decimal number overflow")
|
|
}
|
|
val *= 10
|
|
val += add
|
|
rst.advance(1)
|
|
}
|
|
return val
|
|
}
|
|
|
|
// oldRustToString demangles a Rust symbol using the old demangling.
|
|
// The second result reports whether this is a valid Rust mangled name.
|
|
func oldRustToString(name string, options []Option) (string, bool) {
|
|
max := 0
|
|
for _, o := range options {
|
|
if isMaxLength(o) {
|
|
max = maxLength(o)
|
|
}
|
|
}
|
|
|
|
// We know that the string starts with _ZN.
|
|
name = name[3:]
|
|
|
|
hexDigit := func(c byte) (byte, bool) {
|
|
switch {
|
|
case c >= '0' && c <= '9':
|
|
return c - '0', true
|
|
case c >= 'a' && c <= 'f':
|
|
return c - 'a' + 10, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
// We know that the strings end with "17h" followed by 16 characters
|
|
// followed by "E". We check that the 16 characters are all hex digits.
|
|
// Also the hex digits must contain at least 5 distinct digits.
|
|
seen := uint16(0)
|
|
for i := len(name) - 17; i < len(name)-1; i++ {
|
|
digit, ok := hexDigit(name[i])
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
seen |= 1 << digit
|
|
}
|
|
if bits.OnesCount16(seen) < 5 {
|
|
return "", false
|
|
}
|
|
name = name[:len(name)-20]
|
|
|
|
// The name is a sequence of length-preceded identifiers.
|
|
var sb strings.Builder
|
|
for len(name) > 0 {
|
|
if max > 0 && sb.Len() > max {
|
|
break
|
|
}
|
|
|
|
if !isDigit(name[0]) {
|
|
return "", false
|
|
}
|
|
|
|
val := 0
|
|
for len(name) > 0 && isDigit(name[0]) {
|
|
add := int(name[0] - '0')
|
|
if val >= math.MaxInt32/10-add {
|
|
return "", false
|
|
}
|
|
val *= 10
|
|
val += add
|
|
name = name[1:]
|
|
}
|
|
|
|
// An optional trailing underscore can separate the
|
|
// length from the identifier.
|
|
if len(name) > 0 && name[0] == '_' {
|
|
name = name[1:]
|
|
val--
|
|
}
|
|
|
|
if len(name) < val {
|
|
return "", false
|
|
}
|
|
|
|
id := name[:val]
|
|
name = name[val:]
|
|
|
|
if sb.Len() > 0 {
|
|
sb.WriteString("::")
|
|
}
|
|
|
|
// Ignore leading underscores preceding escape sequences.
|
|
if strings.HasPrefix(id, "_$") {
|
|
id = id[1:]
|
|
}
|
|
|
|
// The identifier can have escape sequences.
|
|
escape:
|
|
for len(id) > 0 {
|
|
switch c := id[0]; c {
|
|
case '$':
|
|
codes := map[string]byte{
|
|
"SP": '@',
|
|
"BP": '*',
|
|
"RF": '&',
|
|
"LT": '<',
|
|
"GT": '>',
|
|
"LP": '(',
|
|
"RP": ')',
|
|
}
|
|
|
|
valid := true
|
|
if len(id) > 2 && id[1] == 'C' && id[2] == '$' {
|
|
sb.WriteByte(',')
|
|
id = id[3:]
|
|
} else if len(id) > 4 && id[1] == 'u' && id[4] == '$' {
|
|
dig1, ok1 := hexDigit(id[2])
|
|
dig2, ok2 := hexDigit(id[3])
|
|
val := (dig1 << 4) | dig2
|
|
if !ok1 || !ok2 || dig1 > 7 || val < ' ' {
|
|
valid = false
|
|
} else {
|
|
sb.WriteByte(val)
|
|
id = id[5:]
|
|
}
|
|
} else if len(id) > 3 && id[3] == '$' {
|
|
if code, ok := codes[id[1:3]]; !ok {
|
|
valid = false
|
|
} else {
|
|
sb.WriteByte(code)
|
|
id = id[4:]
|
|
}
|
|
} else {
|
|
valid = false
|
|
}
|
|
if !valid {
|
|
sb.WriteString(id)
|
|
break escape
|
|
}
|
|
case '.':
|
|
if strings.HasPrefix(id, "..") {
|
|
sb.WriteString("::")
|
|
id = id[2:]
|
|
} else {
|
|
sb.WriteByte(c)
|
|
id = id[1:]
|
|
}
|
|
default:
|
|
sb.WriteByte(c)
|
|
id = id[1:]
|
|
}
|
|
}
|
|
}
|
|
|
|
s := sb.String()
|
|
if max > 0 && len(s) > max {
|
|
s = s[:max]
|
|
}
|
|
return s, true
|
|
}
|