| //===--- RustDemangle.cpp ---------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file defines a demangler for Rust v0 mangled symbols as specified in | |
| // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Demangle/Demangle.h" | |
| #include "llvm/Demangle/StringViewExtras.h" | |
| #include "llvm/Demangle/Utility.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <limits> | |
| #include <string_view> | |
| using namespace llvm; | |
| using llvm::itanium_demangle::OutputBuffer; | |
| using llvm::itanium_demangle::ScopedOverride; | |
| using llvm::itanium_demangle::starts_with; | |
| namespace { | |
| struct Identifier { | |
| std::string_view Name; | |
| bool Punycode; | |
| bool empty() const { return Name.empty(); } | |
| }; | |
| enum class BasicType { | |
| Bool, | |
| Char, | |
| I8, | |
| I16, | |
| I32, | |
| I64, | |
| I128, | |
| ISize, | |
| U8, | |
| U16, | |
| U32, | |
| U64, | |
| U128, | |
| USize, | |
| F32, | |
| F64, | |
| Str, | |
| Placeholder, | |
| Unit, | |
| Variadic, | |
| Never, | |
| }; | |
| enum class IsInType { | |
| No, | |
| Yes, | |
| }; | |
| enum class LeaveGenericsOpen { | |
| No, | |
| Yes, | |
| }; | |
| class Demangler { | |
| // Maximum recursion level. Used to avoid stack overflow. | |
| size_t MaxRecursionLevel; | |
| // Current recursion level. | |
| size_t RecursionLevel; | |
| size_t BoundLifetimes; | |
| // Input string that is being demangled with "_R" prefix removed. | |
| std::string_view Input; | |
| // Position in the input string. | |
| size_t Position; | |
| // When true, print methods append the output to the stream. | |
| // When false, the output is suppressed. | |
| bool Print; | |
| // True if an error occurred. | |
| bool Error; | |
| public: | |
| // Demangled output. | |
| OutputBuffer Output; | |
| Demangler(size_t MaxRecursionLevel = 500); | |
| bool demangle(std::string_view MangledName); | |
| private: | |
| bool demanglePath(IsInType Type, | |
| LeaveGenericsOpen LeaveOpen = LeaveGenericsOpen::No); | |
| void demangleImplPath(IsInType InType); | |
| void demangleGenericArg(); | |
| void demangleType(); | |
| void demangleFnSig(); | |
| void demangleDynBounds(); | |
| void demangleDynTrait(); | |
| void demangleOptionalBinder(); | |
| void demangleConst(); | |
| void demangleConstInt(); | |
| void demangleConstBool(); | |
| void demangleConstChar(); | |
| template <typename Callable> void demangleBackref(Callable Demangler) { | |
| uint64_t Backref = parseBase62Number(); | |
| if (Error || Backref >= Position) { | |
| Error = true; | |
| return; | |
| } | |
| if (!Print) | |
| return; | |
| ScopedOverride<size_t> SavePosition(Position, Position); | |
| Position = Backref; | |
| Demangler(); | |
| } | |
| Identifier parseIdentifier(); | |
| uint64_t parseOptionalBase62Number(char Tag); | |
| uint64_t parseBase62Number(); | |
| uint64_t parseDecimalNumber(); | |
| uint64_t parseHexNumber(std::string_view &HexDigits); | |
| void print(char C); | |
| void print(std::string_view S); | |
| void printDecimalNumber(uint64_t N); | |
| void printBasicType(BasicType); | |
| void printLifetime(uint64_t Index); | |
| void printIdentifier(Identifier Ident); | |
| char look() const; | |
| char consume(); | |
| bool consumeIf(char Prefix); | |
| bool addAssign(uint64_t &A, uint64_t B); | |
| bool mulAssign(uint64_t &A, uint64_t B); | |
| }; | |
| } // namespace | |
| char *llvm::rustDemangle(std::string_view MangledName) { | |
| // Return early if mangled name doesn't look like a Rust symbol. | |
| if (MangledName.empty() || !starts_with(MangledName, "_R")) | |
| return nullptr; | |
| Demangler D; | |
| if (!D.demangle(MangledName)) { | |
| std::free(D.Output.getBuffer()); | |
| return nullptr; | |
| } | |
| D.Output += '\0'; | |
| return D.Output.getBuffer(); | |
| } | |
| Demangler::Demangler(size_t MaxRecursionLevel) | |
| : MaxRecursionLevel(MaxRecursionLevel) {} | |
| static inline bool isDigit(const char C) { return '0' <= C && C <= '9'; } | |
| static inline bool isHexDigit(const char C) { | |
| return ('0' <= C && C <= '9') || ('a' <= C && C <= 'f'); | |
| } | |
| static inline bool isLower(const char C) { return 'a' <= C && C <= 'z'; } | |
| static inline bool isUpper(const char C) { return 'A' <= C && C <= 'Z'; } | |
| /// Returns true if C is a valid mangled character: <0-9a-zA-Z_>. | |
| static inline bool isValid(const char C) { | |
| return isDigit(C) || isLower(C) || isUpper(C) || C == '_'; | |
| } | |
| // Demangles Rust v0 mangled symbol. Returns true when successful, and false | |
| // otherwise. The demangled symbol is stored in Output field. It is | |
| // responsibility of the caller to free the memory behind the output stream. | |
| // | |
| // <symbol-name> = "_R" <path> [<instantiating-crate>] | |
| bool Demangler::demangle(std::string_view Mangled) { | |
| Position = 0; | |
| Error = false; | |
| Print = true; | |
| RecursionLevel = 0; | |
| BoundLifetimes = 0; | |
| if (!starts_with(Mangled, "_R")) { | |
| Error = true; | |
| return false; | |
| } | |
| Mangled.remove_prefix(2); | |
| size_t Dot = Mangled.find('.'); | |
| Input = Dot == std::string_view::npos ? Mangled : Mangled.substr(0, Dot); | |
| demanglePath(IsInType::No); | |
| if (Position != Input.size()) { | |
| ScopedOverride<bool> SavePrint(Print, false); | |
| demanglePath(IsInType::No); | |
| } | |
| if (Position != Input.size()) | |
| Error = true; | |
| if (Dot != std::string_view::npos) { | |
| print(" ("); | |
| print(Mangled.substr(Dot)); | |
| print(")"); | |
| } | |
| return !Error; | |
| } | |
| // Demangles a path. InType indicates whether a path is inside a type. When | |
| // LeaveOpen is true, a closing `>` after generic arguments is omitted from the | |
| // output. Return value indicates whether generics arguments have been left | |
| // open. | |
| // | |
| // <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" <ns> <path> <identifier> // ...::ident (nested path) | |
| // | "I" <path> {<generic-arg>} "E" // ...<T, U> (generic args) | |
| // | <backref> | |
| // <identifier> = [<disambiguator>] <undisambiguated-identifier> | |
| // <ns> = "C" // closure | |
| // | "S" // shim | |
| // | <A-Z> // other special namespaces | |
| // | <a-z> // internal namespaces | |
| bool Demangler::demanglePath(IsInType InType, LeaveGenericsOpen LeaveOpen) { | |
| if (Error || RecursionLevel >= MaxRecursionLevel) { | |
| Error = true; | |
| return false; | |
| } | |
| ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1); | |
| switch (consume()) { | |
| case 'C': { | |
| parseOptionalBase62Number('s'); | |
| printIdentifier(parseIdentifier()); | |
| break; | |
| } | |
| case 'M': { | |
| demangleImplPath(InType); | |
| print("<"); | |
| demangleType(); | |
| print(">"); | |
| break; | |
| } | |
| case 'X': { | |
| demangleImplPath(InType); | |
| print("<"); | |
| demangleType(); | |
| print(" as "); | |
| demanglePath(IsInType::Yes); | |
| print(">"); | |
| break; | |
| } | |
| case 'Y': { | |
| print("<"); | |
| demangleType(); | |
| print(" as "); | |
| demanglePath(IsInType::Yes); | |
| print(">"); | |
| break; | |
| } | |
| case 'N': { | |
| char NS = consume(); | |
| if (!isLower(NS) && !isUpper(NS)) { | |
| Error = true; | |
| break; | |
| } | |
| demanglePath(InType); | |
| uint64_t Disambiguator = parseOptionalBase62Number('s'); | |
| Identifier Ident = parseIdentifier(); | |
| if (isUpper(NS)) { | |
| // Special namespaces | |
| print("::{"); | |
| if (NS == 'C') | |
| print("closure"); | |
| else if (NS == 'S') | |
| print("shim"); | |
| else | |
| print(NS); | |
| if (!Ident.empty()) { | |
| print(":"); | |
| printIdentifier(Ident); | |
| } | |
| print('#'); | |
| printDecimalNumber(Disambiguator); | |
| print('}'); | |
| } else { | |
| // Implementation internal namespaces. | |
| if (!Ident.empty()) { | |
| print("::"); | |
| printIdentifier(Ident); | |
| } | |
| } | |
| break; | |
| } | |
| case 'I': { | |
| demanglePath(InType); | |
| // Omit "::" when in a type, where it is optional. | |
| if (InType == IsInType::No) | |
| print("::"); | |
| print("<"); | |
| for (size_t I = 0; !Error && !consumeIf('E'); ++I) { | |
| if (I > 0) | |
| print(", "); | |
| demangleGenericArg(); | |
| } | |
| if (LeaveOpen == LeaveGenericsOpen::Yes) | |
| return true; | |
| else | |
| print(">"); | |
| break; | |
| } | |
| case 'B': { | |
| bool IsOpen = false; | |
| demangleBackref([&] { IsOpen = demanglePath(InType, LeaveOpen); }); | |
| return IsOpen; | |
| } | |
| default: | |
| Error = true; | |
| break; | |
| } | |
| return false; | |
| } | |
| // <impl-path> = [<disambiguator>] <path> | |
| // <disambiguator> = "s" <base-62-number> | |
| void Demangler::demangleImplPath(IsInType InType) { | |
| ScopedOverride<bool> SavePrint(Print, false); | |
| parseOptionalBase62Number('s'); | |
| demanglePath(InType); | |
| } | |
| // <generic-arg> = <lifetime> | |
| // | <type> | |
| // | "K" <const> | |
| // <lifetime> = "L" <base-62-number> | |
| void Demangler::demangleGenericArg() { | |
| if (consumeIf('L')) | |
| printLifetime(parseBase62Number()); | |
| else if (consumeIf('K')) | |
| demangleConst(); | |
| else | |
| demangleType(); | |
| } | |
| // <basic-type> = "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 | |
| // | "s" // i16 | |
| // | "t" // u16 | |
| // | "u" // () | |
| // | "v" // ... | |
| // | "x" // i64 | |
| // | "y" // u64 | |
| // | "z" // ! | |
| // | "p" // placeholder (e.g. for generic params), shown as _ | |
| static bool parseBasicType(char C, BasicType &Type) { | |
| switch (C) { | |
| case 'a': | |
| Type = BasicType::I8; | |
| return true; | |
| case 'b': | |
| Type = BasicType::Bool; | |
| return true; | |
| case 'c': | |
| Type = BasicType::Char; | |
| return true; | |
| case 'd': | |
| Type = BasicType::F64; | |
| return true; | |
| case 'e': | |
| Type = BasicType::Str; | |
| return true; | |
| case 'f': | |
| Type = BasicType::F32; | |
| return true; | |
| case 'h': | |
| Type = BasicType::U8; | |
| return true; | |
| case 'i': | |
| Type = BasicType::ISize; | |
| return true; | |
| case 'j': | |
| Type = BasicType::USize; | |
| return true; | |
| case 'l': | |
| Type = BasicType::I32; | |
| return true; | |
| case 'm': | |
| Type = BasicType::U32; | |
| return true; | |
| case 'n': | |
| Type = BasicType::I128; | |
| return true; | |
| case 'o': | |
| Type = BasicType::U128; | |
| return true; | |
| case 'p': | |
| Type = BasicType::Placeholder; | |
| return true; | |
| case 's': | |
| Type = BasicType::I16; | |
| return true; | |
| case 't': | |
| Type = BasicType::U16; | |
| return true; | |
| case 'u': | |
| Type = BasicType::Unit; | |
| return true; | |
| case 'v': | |
| Type = BasicType::Variadic; | |
| return true; | |
| case 'x': | |
| Type = BasicType::I64; | |
| return true; | |
| case 'y': | |
| Type = BasicType::U64; | |
| return true; | |
| case 'z': | |
| Type = BasicType::Never; | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| void Demangler::printBasicType(BasicType Type) { | |
| switch (Type) { | |
| case BasicType::Bool: | |
| print("bool"); | |
| break; | |
| case BasicType::Char: | |
| print("char"); | |
| break; | |
| case BasicType::I8: | |
| print("i8"); | |
| break; | |
| case BasicType::I16: | |
| print("i16"); | |
| break; | |
| case BasicType::I32: | |
| print("i32"); | |
| break; | |
| case BasicType::I64: | |
| print("i64"); | |
| break; | |
| case BasicType::I128: | |
| print("i128"); | |
| break; | |
| case BasicType::ISize: | |
| print("isize"); | |
| break; | |
| case BasicType::U8: | |
| print("u8"); | |
| break; | |
| case BasicType::U16: | |
| print("u16"); | |
| break; | |
| case BasicType::U32: | |
| print("u32"); | |
| break; | |
| case BasicType::U64: | |
| print("u64"); | |
| break; | |
| case BasicType::U128: | |
| print("u128"); | |
| break; | |
| case BasicType::USize: | |
| print("usize"); | |
| break; | |
| case BasicType::F32: | |
| print("f32"); | |
| break; | |
| case BasicType::F64: | |
| print("f64"); | |
| break; | |
| case BasicType::Str: | |
| print("str"); | |
| break; | |
| case BasicType::Placeholder: | |
| print("_"); | |
| break; | |
| case BasicType::Unit: | |
| print("()"); | |
| break; | |
| case BasicType::Variadic: | |
| print("..."); | |
| break; | |
| case BasicType::Never: | |
| print("!"); | |
| break; | |
| } | |
| } | |
| // <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> // backref | |
| void Demangler::demangleType() { | |
| if (Error || RecursionLevel >= MaxRecursionLevel) { | |
| Error = true; | |
| return; | |
| } | |
| ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1); | |
| size_t Start = Position; | |
| char C = consume(); | |
| BasicType Type; | |
| if (parseBasicType(C, Type)) | |
| return printBasicType(Type); | |
| switch (C) { | |
| case 'A': | |
| print("["); | |
| demangleType(); | |
| print("; "); | |
| demangleConst(); | |
| print("]"); | |
| break; | |
| case 'S': | |
| print("["); | |
| demangleType(); | |
| print("]"); | |
| break; | |
| case 'T': { | |
| print("("); | |
| size_t I = 0; | |
| for (; !Error && !consumeIf('E'); ++I) { | |
| if (I > 0) | |
| print(", "); | |
| demangleType(); | |
| } | |
| if (I == 1) | |
| print(","); | |
| print(")"); | |
| break; | |
| } | |
| case 'R': | |
| case 'Q': | |
| print('&'); | |
| if (consumeIf('L')) { | |
| if (auto Lifetime = parseBase62Number()) { | |
| printLifetime(Lifetime); | |
| print(' '); | |
| } | |
| } | |
| if (C == 'Q') | |
| print("mut "); | |
| demangleType(); | |
| break; | |
| case 'P': | |
| print("*const "); | |
| demangleType(); | |
| break; | |
| case 'O': | |
| print("*mut "); | |
| demangleType(); | |
| break; | |
| case 'F': | |
| demangleFnSig(); | |
| break; | |
| case 'D': | |
| demangleDynBounds(); | |
| if (consumeIf('L')) { | |
| if (auto Lifetime = parseBase62Number()) { | |
| print(" + "); | |
| printLifetime(Lifetime); | |
| } | |
| } else { | |
| Error = true; | |
| } | |
| break; | |
| case 'B': | |
| demangleBackref([&] { demangleType(); }); | |
| break; | |
| default: | |
| Position = Start; | |
| demanglePath(IsInType::Yes); | |
| break; | |
| } | |
| } | |
| // <fn-sig> := [<binder>] ["U"] ["K" <abi>] {<type>} "E" <type> | |
| // <abi> = "C" | |
| // | <undisambiguated-identifier> | |
| void Demangler::demangleFnSig() { | |
| ScopedOverride<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes); | |
| demangleOptionalBinder(); | |
| if (consumeIf('U')) | |
| print("unsafe "); | |
| if (consumeIf('K')) { | |
| print("extern \""); | |
| if (consumeIf('C')) { | |
| print("C"); | |
| } else { | |
| Identifier Ident = parseIdentifier(); | |
| if (Ident.Punycode) | |
| Error = true; | |
| for (char C : Ident.Name) { | |
| // When mangling ABI string, the "-" is replaced with "_". | |
| if (C == '_') | |
| C = '-'; | |
| print(C); | |
| } | |
| } | |
| print("\" "); | |
| } | |
| print("fn("); | |
| for (size_t I = 0; !Error && !consumeIf('E'); ++I) { | |
| if (I > 0) | |
| print(", "); | |
| demangleType(); | |
| } | |
| print(")"); | |
| if (consumeIf('u')) { | |
| // Skip the unit type from the output. | |
| } else { | |
| print(" -> "); | |
| demangleType(); | |
| } | |
| } | |
| // <dyn-bounds> = [<binder>] {<dyn-trait>} "E" | |
| void Demangler::demangleDynBounds() { | |
| ScopedOverride<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes); | |
| print("dyn "); | |
| demangleOptionalBinder(); | |
| for (size_t I = 0; !Error && !consumeIf('E'); ++I) { | |
| if (I > 0) | |
| print(" + "); | |
| demangleDynTrait(); | |
| } | |
| } | |
| // <dyn-trait> = <path> {<dyn-trait-assoc-binding>} | |
| // <dyn-trait-assoc-binding> = "p" <undisambiguated-identifier> <type> | |
| void Demangler::demangleDynTrait() { | |
| bool IsOpen = demanglePath(IsInType::Yes, LeaveGenericsOpen::Yes); | |
| while (!Error && consumeIf('p')) { | |
| if (!IsOpen) { | |
| IsOpen = true; | |
| print('<'); | |
| } else { | |
| print(", "); | |
| } | |
| print(parseIdentifier().Name); | |
| print(" = "); | |
| demangleType(); | |
| } | |
| if (IsOpen) | |
| print(">"); | |
| } | |
| // Demangles optional binder and updates the number of bound lifetimes. | |
| // | |
| // <binder> = "G" <base-62-number> | |
| void Demangler::demangleOptionalBinder() { | |
| uint64_t Binder = parseOptionalBase62Number('G'); | |
| if (Error || Binder == 0) | |
| return; | |
| // In valid inputs each bound lifetime is referenced later. Referencing a | |
| // lifetime requires at least one byte of input. Reject inputs that are too | |
| // short to reference all bound lifetimes. Otherwise demangling of invalid | |
| // binders could generate excessive amounts of output. | |
| if (Binder >= Input.size() - BoundLifetimes) { | |
| Error = true; | |
| return; | |
| } | |
| print("for<"); | |
| for (size_t I = 0; I != Binder; ++I) { | |
| BoundLifetimes += 1; | |
| if (I > 0) | |
| print(", "); | |
| printLifetime(1); | |
| } | |
| print("> "); | |
| } | |
| // <const> = <basic-type> <const-data> | |
| // | "p" // placeholder | |
| // | <backref> | |
| void Demangler::demangleConst() { | |
| if (Error || RecursionLevel >= MaxRecursionLevel) { | |
| Error = true; | |
| return; | |
| } | |
| ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1); | |
| char C = consume(); | |
| BasicType Type; | |
| if (parseBasicType(C, Type)) { | |
| switch (Type) { | |
| case BasicType::I8: | |
| case BasicType::I16: | |
| case BasicType::I32: | |
| case BasicType::I64: | |
| case BasicType::I128: | |
| case BasicType::ISize: | |
| case BasicType::U8: | |
| case BasicType::U16: | |
| case BasicType::U32: | |
| case BasicType::U64: | |
| case BasicType::U128: | |
| case BasicType::USize: | |
| demangleConstInt(); | |
| break; | |
| case BasicType::Bool: | |
| demangleConstBool(); | |
| break; | |
| case BasicType::Char: | |
| demangleConstChar(); | |
| break; | |
| case BasicType::Placeholder: | |
| print('_'); | |
| break; | |
| default: | |
| Error = true; | |
| break; | |
| } | |
| } else if (C == 'B') { | |
| demangleBackref([&] { demangleConst(); }); | |
| } else { | |
| Error = true; | |
| } | |
| } | |
| // <const-data> = ["n"] <hex-number> | |
| void Demangler::demangleConstInt() { | |
| if (consumeIf('n')) | |
| print('-'); | |
| std::string_view HexDigits; | |
| uint64_t Value = parseHexNumber(HexDigits); | |
| if (HexDigits.size() <= 16) { | |
| printDecimalNumber(Value); | |
| } else { | |
| print("0x"); | |
| print(HexDigits); | |
| } | |
| } | |
| // <const-data> = "0_" // false | |
| // | "1_" // true | |
| void Demangler::demangleConstBool() { | |
| std::string_view HexDigits; | |
| parseHexNumber(HexDigits); | |
| if (HexDigits == "0") | |
| print("false"); | |
| else if (HexDigits == "1") | |
| print("true"); | |
| else | |
| Error = true; | |
| } | |
| /// Returns true if CodePoint represents a printable ASCII character. | |
| static bool isAsciiPrintable(uint64_t CodePoint) { | |
| return 0x20 <= CodePoint && CodePoint <= 0x7e; | |
| } | |
| // <const-data> = <hex-number> | |
| void Demangler::demangleConstChar() { | |
| std::string_view HexDigits; | |
| uint64_t CodePoint = parseHexNumber(HexDigits); | |
| if (Error || HexDigits.size() > 6) { | |
| Error = true; | |
| return; | |
| } | |
| print("'"); | |
| switch (CodePoint) { | |
| case '\t': | |
| print(R"(\t)"); | |
| break; | |
| case '\r': | |
| print(R"(\r)"); | |
| break; | |
| case '\n': | |
| print(R"(\n)"); | |
| break; | |
| case '\\': | |
| print(R"(\\)"); | |
| break; | |
| case '"': | |
| print(R"(")"); | |
| break; | |
| case '\'': | |
| print(R"(\')"); | |
| break; | |
| default: | |
| if (isAsciiPrintable(CodePoint)) { | |
| char C = CodePoint; | |
| print(C); | |
| } else { | |
| print(R"(\u{)"); | |
| print(HexDigits); | |
| print('}'); | |
| } | |
| break; | |
| } | |
| print('\''); | |
| } | |
| // <undisambiguated-identifier> = ["u"] <decimal-number> ["_"] <bytes> | |
| Identifier Demangler::parseIdentifier() { | |
| bool Punycode = consumeIf('u'); | |
| uint64_t Bytes = parseDecimalNumber(); | |
| // Underscore resolves the ambiguity when identifier starts with a decimal | |
| // digit or another underscore. | |
| consumeIf('_'); | |
| if (Error || Bytes > Input.size() - Position) { | |
| Error = true; | |
| return {}; | |
| } | |
| std::string_view S = Input.substr(Position, Bytes); | |
| Position += Bytes; | |
| if (!std::all_of(S.begin(), S.end(), isValid)) { | |
| Error = true; | |
| return {}; | |
| } | |
| return {S, Punycode}; | |
| } | |
| // Parses optional base 62 number. The presence of a number is determined using | |
| // Tag. Returns 0 when tag is absent and parsed value + 1 otherwise | |
| // | |
| // This function is intended for parsing disambiguators and binders which when | |
| // not present have their value interpreted as 0, and otherwise as decoded | |
| // value + 1. For example for binders, value for "G_" is 1, for "G0_" value is | |
| // 2. When "G" is absent value is 0. | |
| uint64_t Demangler::parseOptionalBase62Number(char Tag) { | |
| if (!consumeIf(Tag)) | |
| return 0; | |
| uint64_t N = parseBase62Number(); | |
| if (Error || !addAssign(N, 1)) | |
| return 0; | |
| return N; | |
| } | |
| // Parses base 62 number with <0-9a-zA-Z> as digits. Number is terminated by | |
| // "_". All values are offset by 1, so that "_" encodes 0, "0_" encodes 1, | |
| // "1_" encodes 2, etc. | |
| // | |
| // <base-62-number> = {<0-9a-zA-Z>} "_" | |
| uint64_t Demangler::parseBase62Number() { | |
| if (consumeIf('_')) | |
| return 0; | |
| uint64_t Value = 0; | |
| while (true) { | |
| uint64_t Digit; | |
| char C = consume(); | |
| if (C == '_') { | |
| break; | |
| } else if (isDigit(C)) { | |
| Digit = C - '0'; | |
| } else if (isLower(C)) { | |
| Digit = 10 + (C - 'a'); | |
| } else if (isUpper(C)) { | |
| Digit = 10 + 26 + (C - 'A'); | |
| } else { | |
| Error = true; | |
| return 0; | |
| } | |
| if (!mulAssign(Value, 62)) | |
| return 0; | |
| if (!addAssign(Value, Digit)) | |
| return 0; | |
| } | |
| if (!addAssign(Value, 1)) | |
| return 0; | |
| return Value; | |
| } | |
| // Parses a decimal number that had been encoded without any leading zeros. | |
| // | |
| // <decimal-number> = "0" | |
| // | <1-9> {<0-9>} | |
| uint64_t Demangler::parseDecimalNumber() { | |
| char C = look(); | |
| if (!isDigit(C)) { | |
| Error = true; | |
| return 0; | |
| } | |
| if (C == '0') { | |
| consume(); | |
| return 0; | |
| } | |
| uint64_t Value = 0; | |
| while (isDigit(look())) { | |
| if (!mulAssign(Value, 10)) { | |
| Error = true; | |
| return 0; | |
| } | |
| uint64_t D = consume() - '0'; | |
| if (!addAssign(Value, D)) | |
| return 0; | |
| } | |
| return Value; | |
| } | |
| // Parses a hexadecimal number with <0-9a-f> as a digits. Returns the parsed | |
| // value and stores hex digits in HexDigits. The return value is unspecified if | |
| // HexDigits.size() > 16. | |
| // | |
| // <hex-number> = "0_" | |
| // | <1-9a-f> {<0-9a-f>} "_" | |
| uint64_t Demangler::parseHexNumber(std::string_view &HexDigits) { | |
| size_t Start = Position; | |
| uint64_t Value = 0; | |
| if (!isHexDigit(look())) | |
| Error = true; | |
| if (consumeIf('0')) { | |
| if (!consumeIf('_')) | |
| Error = true; | |
| } else { | |
| while (!Error && !consumeIf('_')) { | |
| char C = consume(); | |
| Value *= 16; | |
| if (isDigit(C)) | |
| Value += C - '0'; | |
| else if ('a' <= C && C <= 'f') | |
| Value += 10 + (C - 'a'); | |
| else | |
| Error = true; | |
| } | |
| } | |
| if (Error) { | |
| HexDigits = std::string_view(); | |
| return 0; | |
| } | |
| size_t End = Position - 1; | |
| assert(Start < End); | |
| HexDigits = Input.substr(Start, End - Start); | |
| return Value; | |
| } | |
| void Demangler::print(char C) { | |
| if (Error || !Print) | |
| return; | |
| Output += C; | |
| } | |
| void Demangler::print(std::string_view S) { | |
| if (Error || !Print) | |
| return; | |
| Output += S; | |
| } | |
| void Demangler::printDecimalNumber(uint64_t N) { | |
| if (Error || !Print) | |
| return; | |
| Output << N; | |
| } | |
| // Prints a lifetime. An index 0 always represents an erased lifetime. Indices | |
| // starting from 1, are De Bruijn indices, referring to higher-ranked lifetimes | |
| // bound by one of the enclosing binders. | |
| void Demangler::printLifetime(uint64_t Index) { | |
| if (Index == 0) { | |
| print("'_"); | |
| return; | |
| } | |
| if (Index - 1 >= BoundLifetimes) { | |
| Error = true; | |
| return; | |
| } | |
| uint64_t Depth = BoundLifetimes - Index; | |
| print('\''); | |
| if (Depth < 26) { | |
| char C = 'a' + Depth; | |
| print(C); | |
| } else { | |
| print('z'); | |
| printDecimalNumber(Depth - 26 + 1); | |
| } | |
| } | |
| static inline bool decodePunycodeDigit(char C, size_t &Value) { | |
| if (isLower(C)) { | |
| Value = C - 'a'; | |
| return true; | |
| } | |
| if (isDigit(C)) { | |
| Value = 26 + (C - '0'); | |
| return true; | |
| } | |
| return false; | |
| } | |
| static void removeNullBytes(OutputBuffer &Output, size_t StartIdx) { | |
| char *Buffer = Output.getBuffer(); | |
| char *Start = Buffer + StartIdx; | |
| char *End = Buffer + Output.getCurrentPosition(); | |
| Output.setCurrentPosition(std::remove(Start, End, '\0') - Buffer); | |
| } | |
| // Encodes code point as UTF-8 and stores results in Output. Returns false if | |
| // CodePoint is not a valid unicode scalar value. | |
| static inline bool encodeUTF8(size_t CodePoint, char *Output) { | |
| if (0xD800 <= CodePoint && CodePoint <= 0xDFFF) | |
| return false; | |
| if (CodePoint <= 0x7F) { | |
| Output[0] = CodePoint; | |
| return true; | |
| } | |
| if (CodePoint <= 0x7FF) { | |
| Output[0] = 0xC0 | ((CodePoint >> 6) & 0x3F); | |
| Output[1] = 0x80 | (CodePoint & 0x3F); | |
| return true; | |
| } | |
| if (CodePoint <= 0xFFFF) { | |
| Output[0] = 0xE0 | (CodePoint >> 12); | |
| Output[1] = 0x80 | ((CodePoint >> 6) & 0x3F); | |
| Output[2] = 0x80 | (CodePoint & 0x3F); | |
| return true; | |
| } | |
| if (CodePoint <= 0x10FFFF) { | |
| Output[0] = 0xF0 | (CodePoint >> 18); | |
| Output[1] = 0x80 | ((CodePoint >> 12) & 0x3F); | |
| Output[2] = 0x80 | ((CodePoint >> 6) & 0x3F); | |
| Output[3] = 0x80 | (CodePoint & 0x3F); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Decodes string encoded using punycode and appends results to Output. | |
| // Returns true if decoding was successful. | |
| static bool decodePunycode(std::string_view Input, OutputBuffer &Output) { | |
| size_t OutputSize = Output.getCurrentPosition(); | |
| size_t InputIdx = 0; | |
| // Rust uses an underscore as a delimiter. | |
| size_t DelimiterPos = std::string_view::npos; | |
| for (size_t I = 0; I != Input.size(); ++I) | |
| if (Input[I] == '_') | |
| DelimiterPos = I; | |
| if (DelimiterPos != std::string_view::npos) { | |
| // Copy basic code points before the last delimiter to the output. | |
| for (; InputIdx != DelimiterPos; ++InputIdx) { | |
| char C = Input[InputIdx]; | |
| if (!isValid(C)) | |
| return false; | |
| // Code points are padded with zeros while decoding is in progress. | |
| char UTF8[4] = {C}; | |
| Output += std::string_view(UTF8, 4); | |
| } | |
| // Skip over the delimiter. | |
| ++InputIdx; | |
| } | |
| size_t Base = 36; | |
| size_t Skew = 38; | |
| size_t Bias = 72; | |
| size_t N = 0x80; | |
| size_t TMin = 1; | |
| size_t TMax = 26; | |
| size_t Damp = 700; | |
| auto Adapt = [&](size_t Delta, size_t NumPoints) { | |
| Delta /= Damp; | |
| Delta += Delta / NumPoints; | |
| Damp = 2; | |
| size_t K = 0; | |
| while (Delta > (Base - TMin) * TMax / 2) { | |
| Delta /= Base - TMin; | |
| K += Base; | |
| } | |
| return K + (((Base - TMin + 1) * Delta) / (Delta + Skew)); | |
| }; | |
| // Main decoding loop. | |
| for (size_t I = 0; InputIdx != Input.size(); I += 1) { | |
| size_t OldI = I; | |
| size_t W = 1; | |
| size_t Max = std::numeric_limits<size_t>::max(); | |
| for (size_t K = Base; true; K += Base) { | |
| if (InputIdx == Input.size()) | |
| return false; | |
| char C = Input[InputIdx++]; | |
| size_t Digit = 0; | |
| if (!decodePunycodeDigit(C, Digit)) | |
| return false; | |
| if (Digit > (Max - I) / W) | |
| return false; | |
| I += Digit * W; | |
| size_t T; | |
| if (K <= Bias) | |
| T = TMin; | |
| else if (K >= Bias + TMax) | |
| T = TMax; | |
| else | |
| T = K - Bias; | |
| if (Digit < T) | |
| break; | |
| if (W > Max / (Base - T)) | |
| return false; | |
| W *= (Base - T); | |
| } | |
| size_t NumPoints = (Output.getCurrentPosition() - OutputSize) / 4 + 1; | |
| Bias = Adapt(I - OldI, NumPoints); | |
| if (I / NumPoints > Max - N) | |
| return false; | |
| N += I / NumPoints; | |
| I = I % NumPoints; | |
| // Insert N at position I in the output. | |
| char UTF8[4] = {}; | |
| if (!encodeUTF8(N, UTF8)) | |
| return false; | |
| Output.insert(OutputSize + I * 4, UTF8, 4); | |
| } | |
| removeNullBytes(Output, OutputSize); | |
| return true; | |
| } | |
| void Demangler::printIdentifier(Identifier Ident) { | |
| if (Error || !Print) | |
| return; | |
| if (Ident.Punycode) { | |
| if (!decodePunycode(Ident.Name, Output)) | |
| Error = true; | |
| } else { | |
| print(Ident.Name); | |
| } | |
| } | |
| char Demangler::look() const { | |
| if (Error || Position >= Input.size()) | |
| return 0; | |
| return Input[Position]; | |
| } | |
| char Demangler::consume() { | |
| if (Error || Position >= Input.size()) { | |
| Error = true; | |
| return 0; | |
| } | |
| return Input[Position++]; | |
| } | |
| bool Demangler::consumeIf(char Prefix) { | |
| if (Error || Position >= Input.size() || Input[Position] != Prefix) | |
| return false; | |
| Position += 1; | |
| return true; | |
| } | |
| /// Computes A + B. When computation wraps around sets the error and returns | |
| /// false. Otherwise assigns the result to A and returns true. | |
| bool Demangler::addAssign(uint64_t &A, uint64_t B) { | |
| if (A > std::numeric_limits<uint64_t>::max() - B) { | |
| Error = true; | |
| return false; | |
| } | |
| A += B; | |
| return true; | |
| } | |
| /// Computes A * B. When computation wraps around sets the error and returns | |
| /// false. Otherwise assigns the result to A and returns true. | |
| bool Demangler::mulAssign(uint64_t &A, uint64_t B) { | |
| if (B != 0 && A > std::numeric_limits<uint64_t>::max() / B) { | |
| Error = true; | |
| return false; | |
| } | |
| A *= B; | |
| return true; | |
| } |