| // 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 |
| } |