mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3681 lines
115 KiB
C++
3681 lines
115 KiB
C++
//===--- Demangle.cpp - Swift Name Demangling -----------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements declaration name demangling in Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/Demangle.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/Strings.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/Punycode.h"
|
|
#include "swift/Basic/UUID.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include <functional>
|
|
#include <vector>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
using namespace swift;
|
|
using namespace Demangle;
|
|
|
|
[[noreturn]]
|
|
static void unreachable(const char *Message) {
|
|
fprintf(stderr, "fatal error: %s\n", Message);
|
|
std::abort();
|
|
}
|
|
|
|
DemanglerPrinter &DemanglerPrinter::operator<<(unsigned long long n) & {
|
|
char buffer[32];
|
|
snprintf(buffer, 32, "%llu", n);
|
|
Stream.append(buffer);
|
|
return *this;
|
|
}
|
|
DemanglerPrinter &DemanglerPrinter::operator<<(long long n) & {
|
|
char buffer[32];
|
|
snprintf(buffer, 32, "%lld",n);
|
|
Stream.append(buffer);
|
|
return *this;
|
|
}
|
|
|
|
namespace {
|
|
struct QuotedString {
|
|
std::string Value;
|
|
|
|
explicit QuotedString(std::string Value) : Value(Value) {}
|
|
};
|
|
|
|
|
|
DemanglerPrinter &operator<<(DemanglerPrinter &printer,
|
|
const QuotedString &QS) {
|
|
printer << '"';
|
|
for (auto C : QS.Value) {
|
|
switch (C) {
|
|
case '\\': printer << "\\\\"; break;
|
|
case '\t': printer << "\\t"; break;
|
|
case '\n': printer << "\\n"; break;
|
|
case '\r': printer << "\\r"; break;
|
|
case '"': printer << "\\\""; break;
|
|
case '\'': printer << '\''; break; // no need to escape these
|
|
case '\0': printer << "\\0"; break;
|
|
default:
|
|
auto c = static_cast<char>(C);
|
|
// Other ASCII control characters should get escaped.
|
|
if (c < 0x20 || c == 0x7F) {
|
|
static const char Hexdigit[] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'A', 'B', 'C', 'D', 'E', 'F'
|
|
};
|
|
printer << "\\x" << Hexdigit[c >> 4] << Hexdigit[c & 0xF];
|
|
} else {
|
|
printer << c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
printer << '"';
|
|
return printer;
|
|
}
|
|
|
|
|
|
} // end unnamed namespace
|
|
|
|
Node::~Node() {
|
|
switch (NodePayloadKind) {
|
|
case PayloadKind::None: return;
|
|
case PayloadKind::Index: return;
|
|
case PayloadKind::Text: TextPayload.~basic_string(); return;
|
|
}
|
|
unreachable("bad payload kind");
|
|
}
|
|
|
|
namespace {
|
|
struct FindPtr {
|
|
FindPtr(Node *v) : Target(v) {}
|
|
bool operator()(NodePointer sp) const {
|
|
return sp.get() == Target;
|
|
}
|
|
private:
|
|
Node *Target;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static bool isStartOfIdentifier(char c) {
|
|
if (c >= '0' && c <= '9')
|
|
return true;
|
|
return c == 'o';
|
|
}
|
|
|
|
static bool isStartOfNominalType(char c) {
|
|
switch (c) {
|
|
case 'C':
|
|
case 'V':
|
|
case 'O':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool isStartOfEntity(char c) {
|
|
switch (c) {
|
|
case 'F':
|
|
case 'I':
|
|
case 'v':
|
|
case 'P':
|
|
case 's':
|
|
case 'Z':
|
|
return true;
|
|
default:
|
|
return isStartOfNominalType(c);
|
|
}
|
|
}
|
|
|
|
static Node::Kind nominalTypeMarkerToNodeKind(char c) {
|
|
if (c == 'C')
|
|
return Node::Kind::Class;
|
|
if (c == 'V')
|
|
return Node::Kind::Structure;
|
|
if (c == 'O')
|
|
return Node::Kind::Enum;
|
|
return Node::Kind::Identifier;
|
|
}
|
|
|
|
static std::string archetypeName(Node::IndexType index,
|
|
Node::IndexType depth) {
|
|
std::string str;
|
|
DemanglerPrinter name(str);
|
|
do {
|
|
name << (char)('A' + (index % 26));
|
|
index /= 26;
|
|
} while (index);
|
|
if (depth != 0)
|
|
name << depth;
|
|
return str;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A convenient class for parsing characters out of a string.
|
|
class NameSource {
|
|
StringRef Text;
|
|
public:
|
|
NameSource(StringRef text) : Text(text) {}
|
|
|
|
/// Return whether there are at least len characters remaining.
|
|
bool hasAtLeast(size_t len) { return (len <= Text.size()); }
|
|
|
|
bool isEmpty() { return Text.empty(); }
|
|
explicit operator bool() { return !isEmpty(); }
|
|
|
|
/// Return the next character without claiming it. Asserts that
|
|
/// there is at least one remaining character.
|
|
char peek() { return Text.front(); }
|
|
|
|
/// Claim and return the next character. Asserts that there is at
|
|
/// least one remaining character.
|
|
char next() {
|
|
char c = peek();
|
|
advanceOffset(1);
|
|
return c;
|
|
}
|
|
|
|
/// Claim the next character if it exists and equals the given
|
|
/// character.
|
|
bool nextIf(char c) {
|
|
if (isEmpty() || peek() != c) return false;
|
|
advanceOffset(1);
|
|
return true;
|
|
}
|
|
|
|
/// Claim the next few characters if they exactly match the given string.
|
|
bool nextIf(StringRef str) {
|
|
if (!Text.startswith(str)) return false;
|
|
advanceOffset(str.size());
|
|
return true;
|
|
}
|
|
|
|
/// Return the next len characters without claiming them. Asserts
|
|
/// that there are at least so many characters.
|
|
StringRef slice(size_t len) { return Text.substr(0, len); }
|
|
|
|
/// Return the current string ref without claiming any characters.
|
|
StringRef str() { return Text; }
|
|
|
|
/// Claim the next len characters.
|
|
void advanceOffset(size_t len) {
|
|
Text = Text.substr(len);
|
|
}
|
|
|
|
/// Claim and return all the rest of the characters.
|
|
StringRef getString() {
|
|
auto result = Text;
|
|
advanceOffset(Text.size());
|
|
return result;
|
|
}
|
|
|
|
bool readUntil(char c, std::string &result) {
|
|
llvm::Optional<char> c2;
|
|
while (!isEmpty() && (c2 = peek()).getValue() != c) {
|
|
result.push_back(c2.getValue());
|
|
advanceOffset(1);
|
|
}
|
|
return c2.hasValue() && c2.getValue() == c;
|
|
}
|
|
};
|
|
|
|
static StringRef toString(Directness d) {
|
|
switch (d) {
|
|
case Directness::Direct:
|
|
return "direct";
|
|
case Directness::Indirect:
|
|
return "indirect";
|
|
}
|
|
unreachable("bad directness");
|
|
}
|
|
|
|
static StringRef toString(ValueWitnessKind k) {
|
|
switch (k) {
|
|
case ValueWitnessKind::AllocateBuffer:
|
|
return "allocateBuffer";
|
|
case ValueWitnessKind::AssignWithCopy:
|
|
return "assignWithCopy";
|
|
case ValueWitnessKind::AssignWithTake:
|
|
return "assignWithTake";
|
|
case ValueWitnessKind::DeallocateBuffer:
|
|
return "deallocateBuffer";
|
|
case ValueWitnessKind::Destroy:
|
|
return "destroy";
|
|
case ValueWitnessKind::DestroyBuffer:
|
|
return "destroyBuffer";
|
|
case ValueWitnessKind::InitializeBufferWithCopyOfBuffer:
|
|
return "initializeBufferWithCopyOfBuffer";
|
|
case ValueWitnessKind::InitializeBufferWithCopy:
|
|
return "initializeBufferWithCopy";
|
|
case ValueWitnessKind::InitializeWithCopy:
|
|
return "initializeWithCopy";
|
|
case ValueWitnessKind::InitializeBufferWithTake:
|
|
return "initializeBufferWithTake";
|
|
case ValueWitnessKind::InitializeWithTake:
|
|
return "initializeWithTake";
|
|
case ValueWitnessKind::ProjectBuffer:
|
|
return "projectBuffer";
|
|
case ValueWitnessKind::InitializeBufferWithTakeOfBuffer:
|
|
return "initializeBufferWithTakeOfBuffer";
|
|
case ValueWitnessKind::DestroyArray:
|
|
return "destroyArray";
|
|
case ValueWitnessKind::InitializeArrayWithCopy:
|
|
return "initializeArrayWithCopy";
|
|
case ValueWitnessKind::InitializeArrayWithTakeFrontToBack:
|
|
return "initializeArrayWithTakeFrontToBack";
|
|
case ValueWitnessKind::InitializeArrayWithTakeBackToFront:
|
|
return "initializeArrayWithTakeBackToFront";
|
|
case ValueWitnessKind::StoreExtraInhabitant:
|
|
return "storeExtraInhabitant";
|
|
case ValueWitnessKind::GetExtraInhabitantIndex:
|
|
return "getExtraInhabitantIndex";
|
|
case ValueWitnessKind::GetEnumTag:
|
|
return "getEnumTag";
|
|
case ValueWitnessKind::DestructiveProjectEnumData:
|
|
return "destructiveProjectEnumData";
|
|
}
|
|
unreachable("bad value witness kind");
|
|
}
|
|
|
|
/// The main class for parsing a demangling tree out of a mangled string.
|
|
class Demangler {
|
|
std::vector<NodePointer> Substitutions;
|
|
NameSource Mangled;
|
|
public:
|
|
Demangler(llvm::StringRef mangled) : Mangled(mangled) {}
|
|
|
|
/// Try to demangle a child node of the given kind. If that fails,
|
|
/// return; otherwise add it to the parent.
|
|
#define DEMANGLE_CHILD_OR_RETURN(PARENT, CHILD_KIND) do { \
|
|
auto _node = demangle##CHILD_KIND(); \
|
|
if (!_node) return nullptr; \
|
|
(PARENT)->addChild(std::move(_node)); \
|
|
} while (false)
|
|
|
|
/// Try to demangle a child node of the given kind. If that fails,
|
|
/// return; otherwise add it to the parent.
|
|
#define DEMANGLE_CHILD_AS_NODE_OR_RETURN(PARENT, CHILD_KIND) do { \
|
|
auto _kind = demangle##CHILD_KIND(); \
|
|
if (!_kind.hasValue()) return nullptr; \
|
|
(PARENT)->addChild(NodeFactory::create(Node::Kind::CHILD_KIND, \
|
|
unsigned(*_kind))); \
|
|
} while (false)
|
|
|
|
/// Attempt to demangle the source string. The root node will
|
|
/// always be a Global. Extra characters at the end will be
|
|
/// tolerated (and included as a Suffix node as a child of the
|
|
/// Global).
|
|
///
|
|
/// \return true if the mangling succeeded
|
|
NodePointer demangleTopLevel() {
|
|
if (!Mangled.nextIf("_T"))
|
|
return nullptr;
|
|
|
|
NodePointer topLevel = NodeFactory::create(Node::Kind::Global);
|
|
|
|
// First demangle any specialization prefixes.
|
|
if (Mangled.nextIf("TS")) {
|
|
do {
|
|
DEMANGLE_CHILD_OR_RETURN(topLevel, SpecializedAttribute);
|
|
|
|
// The Substitution header does not share state with the rest
|
|
// of the mangling.
|
|
Substitutions.clear();
|
|
} while (Mangled.nextIf("_TTS"));
|
|
|
|
// Then check that we have a global.
|
|
if (!Mangled.nextIf("_T"))
|
|
return nullptr;
|
|
|
|
} else if (Mangled.nextIf("To")) {
|
|
topLevel->addChild(NodeFactory::create(Node::Kind::ObjCAttribute));
|
|
} else if (Mangled.nextIf("TO")) {
|
|
topLevel->addChild(NodeFactory::create(Node::Kind::NonObjCAttribute));
|
|
} else if (Mangled.nextIf("TD")) {
|
|
topLevel->addChild(NodeFactory::create(Node::Kind::DynamicAttribute));
|
|
} else if (Mangled.nextIf("Td")) {
|
|
topLevel->addChild(NodeFactory::create(
|
|
Node::Kind::DirectMethodReferenceAttribute));
|
|
} else if (Mangled.nextIf("TV")) {
|
|
topLevel->addChild(NodeFactory::create(Node::Kind::VTableAttribute));
|
|
}
|
|
|
|
DEMANGLE_CHILD_OR_RETURN(topLevel, Global);
|
|
|
|
// Add a suffix node if there's anything left unmangled.
|
|
if (!Mangled.isEmpty()) {
|
|
topLevel->addChild(NodeFactory::create(Node::Kind::Suffix,
|
|
Mangled.getString()));
|
|
}
|
|
|
|
return topLevel;
|
|
}
|
|
|
|
NodePointer demangleTypeName() {
|
|
return demangleType();
|
|
}
|
|
|
|
private:
|
|
enum class IsVariadic {
|
|
yes = true, no = false
|
|
};
|
|
|
|
Optional<Directness> demangleDirectness() {
|
|
if (Mangled.nextIf('d'))
|
|
return Directness::Direct;
|
|
if (Mangled.nextIf('i'))
|
|
return Directness::Indirect;
|
|
return None;
|
|
}
|
|
|
|
bool demangleNatural(Node::IndexType &num) {
|
|
if (!Mangled)
|
|
return false;
|
|
char c = Mangled.next();
|
|
if (c < '0' || c > '9')
|
|
return false;
|
|
num = (c - '0');
|
|
while (true) {
|
|
if (!Mangled) {
|
|
return true;
|
|
}
|
|
c = Mangled.peek();
|
|
if (c < '0' || c > '9') {
|
|
return true;
|
|
} else {
|
|
num = (10 * num) + (c - '0');
|
|
}
|
|
Mangled.next();
|
|
}
|
|
}
|
|
|
|
bool demangleBuiltinSize(Node::IndexType &num) {
|
|
if (!demangleNatural(num))
|
|
return false;
|
|
if (Mangled.nextIf('_'))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
Optional<ValueWitnessKind> demangleValueWitnessKind() {
|
|
if (!Mangled)
|
|
return None;
|
|
char c1 = Mangled.next();
|
|
if (!Mangled)
|
|
return None;
|
|
char c2 = Mangled.next();
|
|
if (c1 == 'a' && c2 == 'l')
|
|
return ValueWitnessKind::AllocateBuffer;
|
|
if (c1 == 'c' && c2 == 'a')
|
|
return ValueWitnessKind::AssignWithCopy;
|
|
if (c1 == 't' && c2 == 'a')
|
|
return ValueWitnessKind::AssignWithTake;
|
|
if (c1 == 'd' && c2 == 'e')
|
|
return ValueWitnessKind::DeallocateBuffer;
|
|
if (c1 == 'x' && c2 == 'x')
|
|
return ValueWitnessKind::Destroy;
|
|
if (c1 == 'X' && c2 == 'X')
|
|
return ValueWitnessKind::DestroyBuffer;
|
|
if (c1 == 'C' && c2 == 'P')
|
|
return ValueWitnessKind::InitializeBufferWithCopyOfBuffer;
|
|
if (c1 == 'C' && c2 == 'p')
|
|
return ValueWitnessKind::InitializeBufferWithCopy;
|
|
if (c1 == 'c' && c2 == 'p')
|
|
return ValueWitnessKind::InitializeWithCopy;
|
|
if (c1 == 'C' && c2 == 'c')
|
|
return ValueWitnessKind::InitializeArrayWithCopy;
|
|
if (c1 == 'T' && c2 == 'K')
|
|
return ValueWitnessKind::InitializeBufferWithTakeOfBuffer;
|
|
if (c1 == 'T' && c2 == 'k')
|
|
return ValueWitnessKind::InitializeBufferWithTake;
|
|
if (c1 == 't' && c2 == 'k')
|
|
return ValueWitnessKind::InitializeWithTake;
|
|
if (c1 == 'T' && c2 == 't')
|
|
return ValueWitnessKind::InitializeArrayWithTakeFrontToBack;
|
|
if (c1 == 't' && c2 == 'T')
|
|
return ValueWitnessKind::InitializeArrayWithTakeBackToFront;
|
|
if (c1 == 'p' && c2 == 'r')
|
|
return ValueWitnessKind::ProjectBuffer;
|
|
if (c1 == 'X' && c2 == 'x')
|
|
return ValueWitnessKind::DestroyArray;
|
|
if (c1 == 'x' && c2 == 's')
|
|
return ValueWitnessKind::StoreExtraInhabitant;
|
|
if (c1 == 'x' && c2 == 'g')
|
|
return ValueWitnessKind::GetExtraInhabitantIndex;
|
|
if (c1 == 'u' && c2 == 'g')
|
|
return ValueWitnessKind::GetEnumTag;
|
|
if (c1 == 'u' && c2 == 'p')
|
|
return ValueWitnessKind::DestructiveProjectEnumData;
|
|
return None;
|
|
}
|
|
|
|
NodePointer demangleGlobal() {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
|
|
// Type metadata.
|
|
if (Mangled.nextIf('M')) {
|
|
if (Mangled.nextIf('P')) {
|
|
auto pattern =
|
|
NodeFactory::create(Node::Kind::GenericTypeMetadataPattern);
|
|
DEMANGLE_CHILD_OR_RETURN(pattern, Type);
|
|
return pattern;
|
|
}
|
|
if (Mangled.nextIf('a')) {
|
|
auto accessor =
|
|
NodeFactory::create(Node::Kind::TypeMetadataAccessFunction);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, Type);
|
|
return accessor;
|
|
}
|
|
if (Mangled.nextIf('L')) {
|
|
auto cache = NodeFactory::create(Node::Kind::TypeMetadataLazyCache);
|
|
DEMANGLE_CHILD_OR_RETURN(cache, Type);
|
|
return cache;
|
|
}
|
|
if (Mangled.nextIf('m')) {
|
|
auto metaclass = NodeFactory::create(Node::Kind::Metaclass);
|
|
DEMANGLE_CHILD_OR_RETURN(metaclass, Type);
|
|
return metaclass;
|
|
}
|
|
if (Mangled.nextIf('n')) {
|
|
auto nominalType =
|
|
NodeFactory::create(Node::Kind::NominalTypeDescriptor);
|
|
DEMANGLE_CHILD_OR_RETURN(nominalType, Type);
|
|
return nominalType;
|
|
}
|
|
if (Mangled.nextIf('f')) {
|
|
auto metadata = NodeFactory::create(Node::Kind::FullTypeMetadata);
|
|
DEMANGLE_CHILD_OR_RETURN(metadata, Type);
|
|
return metadata;
|
|
}
|
|
if (Mangled.nextIf('p')) {
|
|
auto metadata = NodeFactory::create(Node::Kind::ProtocolDescriptor);
|
|
DEMANGLE_CHILD_OR_RETURN(metadata, ProtocolName);
|
|
return metadata;
|
|
}
|
|
auto metadata = NodeFactory::create(Node::Kind::TypeMetadata);
|
|
DEMANGLE_CHILD_OR_RETURN(metadata, Type);
|
|
return metadata;
|
|
}
|
|
|
|
// Partial application thunks.
|
|
if (Mangled.nextIf('P')) {
|
|
if (!Mangled.nextIf('A')) return nullptr;
|
|
Node::Kind kind = Node::Kind::PartialApplyForwarder;
|
|
if (Mangled.nextIf('o'))
|
|
kind = Node::Kind::PartialApplyObjCForwarder;
|
|
auto forwarder = NodeFactory::create(kind);
|
|
if (Mangled.nextIf("__T"))
|
|
DEMANGLE_CHILD_OR_RETURN(forwarder, Global);
|
|
return forwarder;
|
|
}
|
|
|
|
// Top-level types, for various consumers.
|
|
if (Mangled.nextIf('t')) {
|
|
auto type = NodeFactory::create(Node::Kind::TypeMangling);
|
|
DEMANGLE_CHILD_OR_RETURN(type, Type);
|
|
return type;
|
|
}
|
|
|
|
// Value witnesses.
|
|
if (Mangled.nextIf('w')) {
|
|
Optional<ValueWitnessKind> w = demangleValueWitnessKind();
|
|
if (!w.hasValue())
|
|
return nullptr;
|
|
auto witness =
|
|
NodeFactory::create(Node::Kind::ValueWitness, unsigned(w.getValue()));
|
|
DEMANGLE_CHILD_OR_RETURN(witness, Type);
|
|
return witness;
|
|
}
|
|
|
|
// Offsets, value witness tables, and protocol witnesses.
|
|
if (Mangled.nextIf('W')) {
|
|
if (Mangled.nextIf('V')) {
|
|
auto witnessTable = NodeFactory::create(Node::Kind::ValueWitnessTable);
|
|
DEMANGLE_CHILD_OR_RETURN(witnessTable, Type);
|
|
return witnessTable;
|
|
}
|
|
if (Mangled.nextIf('o')) {
|
|
auto witnessTableOffset =
|
|
NodeFactory::create(Node::Kind::WitnessTableOffset);
|
|
DEMANGLE_CHILD_OR_RETURN(witnessTableOffset, Entity);
|
|
return witnessTableOffset;
|
|
}
|
|
if (Mangled.nextIf('v')) {
|
|
auto fieldOffset = NodeFactory::create(Node::Kind::FieldOffset);
|
|
DEMANGLE_CHILD_AS_NODE_OR_RETURN(fieldOffset, Directness);
|
|
DEMANGLE_CHILD_OR_RETURN(fieldOffset, Entity);
|
|
return fieldOffset;
|
|
}
|
|
if (Mangled.nextIf('P')) {
|
|
auto witnessTable =
|
|
NodeFactory::create(Node::Kind::ProtocolWitnessTable);
|
|
DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance);
|
|
return witnessTable;
|
|
}
|
|
if (Mangled.nextIf('G')) {
|
|
auto witnessTable =
|
|
NodeFactory::create(Node::Kind::GenericProtocolWitnessTable);
|
|
DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance);
|
|
return witnessTable;
|
|
}
|
|
if (Mangled.nextIf('I')) {
|
|
auto witnessTable = NodeFactory::create(
|
|
Node::Kind::GenericProtocolWitnessTableInstantiationFunction);
|
|
DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance);
|
|
return witnessTable;
|
|
}
|
|
if (Mangled.nextIf('l')) {
|
|
auto accessor =
|
|
NodeFactory::create(Node::Kind::LazyProtocolWitnessTableAccessor);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, Type);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance);
|
|
return accessor;
|
|
}
|
|
if (Mangled.nextIf('L')) {
|
|
auto accessor =
|
|
NodeFactory::create(Node::Kind::LazyProtocolWitnessTableCacheVariable);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, Type);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance);
|
|
return accessor;
|
|
}
|
|
if (Mangled.nextIf('a')) {
|
|
auto tableTemplate =
|
|
NodeFactory::create(Node::Kind::ProtocolWitnessTableAccessor);
|
|
DEMANGLE_CHILD_OR_RETURN(tableTemplate, ProtocolConformance);
|
|
return tableTemplate;
|
|
}
|
|
if (Mangled.nextIf('t')) {
|
|
auto accessor = NodeFactory::create(
|
|
Node::Kind::AssociatedTypeMetadataAccessor);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, DeclName);
|
|
return accessor;
|
|
}
|
|
if (Mangled.nextIf('T')) {
|
|
auto accessor = NodeFactory::create(
|
|
Node::Kind::AssociatedTypeWitnessTableAccessor);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, DeclName);
|
|
DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolName);
|
|
return accessor;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Other thunks.
|
|
if (Mangled.nextIf('T')) {
|
|
if (Mangled.nextIf('R')) {
|
|
auto thunk = NodeFactory::create(Node::Kind::ReabstractionThunkHelper);
|
|
if (!demangleReabstractSignature(thunk))
|
|
return nullptr;
|
|
return thunk;
|
|
}
|
|
if (Mangled.nextIf('r')) {
|
|
auto thunk = NodeFactory::create(Node::Kind::ReabstractionThunk);
|
|
if (!demangleReabstractSignature(thunk))
|
|
return nullptr;
|
|
return thunk;
|
|
}
|
|
if (Mangled.nextIf('W')) {
|
|
NodePointer thunk = NodeFactory::create(Node::Kind::ProtocolWitness);
|
|
DEMANGLE_CHILD_OR_RETURN(thunk, ProtocolConformance);
|
|
// The entity is mangled in its own generic context.
|
|
DEMANGLE_CHILD_OR_RETURN(thunk, Entity);
|
|
return thunk;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Everything else is just an entity.
|
|
return demangleEntity();
|
|
}
|
|
|
|
NodePointer demangleGenericSpecialization(NodePointer specialization) {
|
|
while (!Mangled.nextIf('_')) {
|
|
// Otherwise, we have another parameter. Demangle the type.
|
|
NodePointer param = NodeFactory::create(Node::Kind::GenericSpecializationParam);
|
|
DEMANGLE_CHILD_OR_RETURN(param, Type);
|
|
|
|
// Then parse any conformances until we find an underscore. Pop off the
|
|
// underscore since it serves as the end of our mangling list.
|
|
while (!Mangled.nextIf('_')) {
|
|
DEMANGLE_CHILD_OR_RETURN(param, ProtocolConformance);
|
|
}
|
|
|
|
// Add the parameter to our specialization list.
|
|
specialization->addChild(param);
|
|
}
|
|
|
|
return specialization;
|
|
}
|
|
|
|
/// TODO: This is an atrocity. Come up with a shorter name.
|
|
#define FUNCSIGSPEC_CREATE_PARAM_KIND(kind) \
|
|
NodeFactory::create(Node::Kind::FunctionSignatureSpecializationParamKind, \
|
|
unsigned(FunctionSigSpecializationParamKind::kind))
|
|
#define FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(payload) \
|
|
NodeFactory::create(Node::Kind::FunctionSignatureSpecializationParamPayload, \
|
|
payload)
|
|
|
|
bool demangleFuncSigSpecializationConstantProp(NodePointer parent) {
|
|
// Then figure out what was actually constant propagated. First check if
|
|
// we have a function.
|
|
if (Mangled.nextIf("fr")) {
|
|
// Demangle the identifier
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name || !Mangled.nextIf('_'))
|
|
return false;
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropFunction));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(name->getText()));
|
|
return true;
|
|
}
|
|
|
|
if (Mangled.nextIf('g')) {
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name || !Mangled.nextIf('_'))
|
|
return false;
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropGlobal));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(name->getText()));
|
|
return true;
|
|
}
|
|
|
|
if (Mangled.nextIf('i')) {
|
|
std::string Str;
|
|
if (!Mangled.readUntil('_', Str) || !Mangled.nextIf('_'))
|
|
return false;
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropInteger));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(Str));
|
|
return true;
|
|
}
|
|
|
|
if (Mangled.nextIf("fl")) {
|
|
std::string Str;
|
|
if (!Mangled.readUntil('_', Str) || !Mangled.nextIf('_'))
|
|
return false;
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropFloat));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(Str));
|
|
return true;
|
|
}
|
|
|
|
if (Mangled.nextIf("s")) {
|
|
// Skip: 'e' encoding 'v' str. encoding is a 0 or 1 and str is a string of
|
|
// length less than or equal to 32. We do not specialize strings with a
|
|
// length greater than 32.
|
|
if (!Mangled.nextIf('e'))
|
|
return false;
|
|
char encoding = Mangled.peek();
|
|
if (encoding != '0' && encoding != '1')
|
|
return false;
|
|
std::string encodingStr;
|
|
if (encoding == '0')
|
|
encodingStr += "u8";
|
|
else
|
|
encodingStr += "u16";
|
|
Mangled.advanceOffset(1);
|
|
|
|
if (!Mangled.nextIf('v'))
|
|
return false;
|
|
NodePointer str = demangleIdentifier();
|
|
if (!str || !Mangled.nextIf('_'))
|
|
return false;
|
|
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropString));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(encodingStr));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(str->getText()));
|
|
return true;
|
|
}
|
|
|
|
unreachable("Unknown constant prop specialization");
|
|
}
|
|
|
|
bool demangleFuncSigSpecializationClosureProp(NodePointer parent) {
|
|
// We don't actually demangle the function or types for now. But we do want
|
|
// to signal that we specialized a closure.
|
|
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ClosureProp));
|
|
parent->addChild(FUNCSIGSPEC_CREATE_PARAM_PAYLOAD(name->getText()));
|
|
|
|
// Then demangle types until we fail.
|
|
NodePointer type;
|
|
while (Mangled.peek() != '_' && (type = demangleType())) {
|
|
parent->addChild(type);
|
|
}
|
|
|
|
// Eat last '_'
|
|
if (!Mangled.nextIf('_'))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
NodePointer
|
|
demangleFunctionSignatureSpecialization(NodePointer specialization) {
|
|
unsigned paramCount = 0;
|
|
|
|
// Until we hit the last '_' in our specialization info...
|
|
while (!Mangled.nextIf('_')) {
|
|
// Create the parameter.
|
|
NodePointer param =
|
|
NodeFactory::create(Node::Kind::FunctionSignatureSpecializationParam,
|
|
paramCount);
|
|
|
|
// First handle options.
|
|
if (Mangled.nextIf("n_")) {
|
|
// Leave the parameter empty.
|
|
} else if (Mangled.nextIf("cp")) {
|
|
if (!demangleFuncSigSpecializationConstantProp(param))
|
|
return nullptr;
|
|
} else if (Mangled.nextIf("cl")) {
|
|
if (!demangleFuncSigSpecializationClosureProp(param))
|
|
return nullptr;
|
|
} else if (Mangled.nextIf("i_")) {
|
|
auto result = FUNCSIGSPEC_CREATE_PARAM_KIND(BoxToValue);
|
|
if (!result)
|
|
return nullptr;
|
|
param->addChild(result);
|
|
} else if (Mangled.nextIf("k_")) {
|
|
auto result = FUNCSIGSPEC_CREATE_PARAM_KIND(BoxToStack);
|
|
if (!result)
|
|
return nullptr;
|
|
param->addChild(result);
|
|
} else {
|
|
// Otherwise handle option sets.
|
|
unsigned Value = 0;
|
|
if (Mangled.nextIf('d')) {
|
|
Value |=
|
|
unsigned(FunctionSigSpecializationParamKind::Dead);
|
|
}
|
|
|
|
if (Mangled.nextIf('g')) {
|
|
Value |=
|
|
unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed);
|
|
}
|
|
|
|
if (Mangled.nextIf('s')) {
|
|
Value |= unsigned(FunctionSigSpecializationParamKind::SROA);
|
|
}
|
|
|
|
if (!Mangled.nextIf('_'))
|
|
return nullptr;
|
|
|
|
if (!Value)
|
|
return nullptr;
|
|
|
|
auto result = NodeFactory::create(
|
|
Node::Kind::FunctionSignatureSpecializationParamKind, Value);
|
|
if (!result)
|
|
return nullptr;
|
|
param->addChild(result);
|
|
}
|
|
|
|
specialization->addChild(param);
|
|
paramCount++;
|
|
}
|
|
|
|
return specialization;
|
|
}
|
|
|
|
#undef FUNCSIGSPEC_CREATE_PARAM_KIND
|
|
#undef FUNCSIGSPEC_CREATE_PARAM_PAYLOAD
|
|
|
|
NodePointer demangleSpecializedAttribute() {
|
|
bool isNotReAbstracted = false;
|
|
if (Mangled.nextIf("g") || (isNotReAbstracted = Mangled.nextIf("r"))) {
|
|
auto spec = NodeFactory::create(isNotReAbstracted ?
|
|
Node::Kind::GenericSpecializationNotReAbstracted :
|
|
Node::Kind::GenericSpecialization);
|
|
// Create a node for the pass id.
|
|
spec->addChild(NodeFactory::create(Node::Kind::SpecializationPassID,
|
|
unsigned(Mangled.next() - 48)));
|
|
// And then mangle the generic specialization.
|
|
return demangleGenericSpecialization(spec);
|
|
}
|
|
if (Mangled.nextIf("f")) {
|
|
auto spec =
|
|
NodeFactory::create(Node::Kind::FunctionSignatureSpecialization);
|
|
|
|
// Add the pass id.
|
|
spec->addChild(NodeFactory::create(Node::Kind::SpecializationPassID,
|
|
unsigned(Mangled.next() - 48)));
|
|
|
|
// Then perform the function signature specialization.
|
|
return demangleFunctionSignatureSpecialization(spec);
|
|
}
|
|
|
|
// We don't know how to handle this specialization.
|
|
return nullptr;
|
|
}
|
|
|
|
NodePointer demangleDeclName() {
|
|
// decl-name ::= local-decl-name
|
|
// local-decl-name ::= 'L' index identifier
|
|
if (Mangled.nextIf('L')) {
|
|
NodePointer discriminator = demangleIndexAsNode();
|
|
if (!discriminator) return nullptr;
|
|
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name) return nullptr;
|
|
|
|
NodePointer localName = NodeFactory::create(Node::Kind::LocalDeclName);
|
|
localName->addChild(std::move(discriminator));
|
|
localName->addChild(std::move(name));
|
|
return localName;
|
|
|
|
} else if (Mangled.nextIf('P')) {
|
|
NodePointer discriminator = demangleIdentifier();
|
|
if (!discriminator) return nullptr;
|
|
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name) return nullptr;
|
|
|
|
auto privateName = NodeFactory::create(Node::Kind::PrivateDeclName);
|
|
privateName->addChildren(std::move(discriminator), std::move(name));
|
|
return privateName;
|
|
}
|
|
|
|
// decl-name ::= identifier
|
|
return demangleIdentifier();
|
|
}
|
|
|
|
NodePointer demangleIdentifier(Optional<Node::Kind> kind = None) {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
|
|
bool isPunycoded = Mangled.nextIf('X');
|
|
std::string decodeBuffer;
|
|
|
|
auto decode = [&](StringRef s) -> StringRef {
|
|
if (!isPunycoded)
|
|
return s;
|
|
if (!Punycode::decodePunycodeUTF8(s, decodeBuffer))
|
|
return {};
|
|
return decodeBuffer;
|
|
};
|
|
|
|
bool isOperator = false;
|
|
if (Mangled.nextIf('o')) {
|
|
isOperator = true;
|
|
// Operator identifiers aren't valid in the contexts that are
|
|
// building more specific identifiers.
|
|
if (kind.hasValue()) return nullptr;
|
|
|
|
char op_mode = Mangled.next();
|
|
switch (op_mode) {
|
|
case 'p':
|
|
kind = Node::Kind::PrefixOperator;
|
|
break;
|
|
case 'P':
|
|
kind = Node::Kind::PostfixOperator;
|
|
break;
|
|
case 'i':
|
|
kind = Node::Kind::InfixOperator;
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (!kind.hasValue()) kind = Node::Kind::Identifier;
|
|
|
|
Node::IndexType length;
|
|
if (!demangleNatural(length))
|
|
return nullptr;
|
|
if (!Mangled.hasAtLeast(length))
|
|
return nullptr;
|
|
|
|
StringRef identifier = Mangled.slice(length);
|
|
Mangled.advanceOffset(length);
|
|
|
|
// Decode Unicode identifiers.
|
|
identifier = decode(identifier);
|
|
if (identifier.empty())
|
|
return nullptr;
|
|
|
|
// Decode operator names.
|
|
std::string opDecodeBuffer;
|
|
if (isOperator) {
|
|
// abcdefghijklmnopqrstuvwxyz
|
|
static const char op_char_table[] = "& @/= > <*!|+?%-~ ^ .";
|
|
|
|
opDecodeBuffer.reserve(identifier.size());
|
|
for (signed char c : identifier) {
|
|
if (c < 0) {
|
|
// Pass through Unicode characters.
|
|
opDecodeBuffer.push_back(c);
|
|
continue;
|
|
}
|
|
if (c < 'a' || c > 'z')
|
|
return nullptr;
|
|
char o = op_char_table[c - 'a'];
|
|
if (o == ' ')
|
|
return nullptr;
|
|
opDecodeBuffer.push_back(o);
|
|
}
|
|
identifier = opDecodeBuffer;
|
|
}
|
|
|
|
return NodeFactory::create(*kind, identifier);
|
|
}
|
|
|
|
bool demangleIndex(Node::IndexType &natural) {
|
|
if (Mangled.nextIf('_')) {
|
|
natural = 0;
|
|
return true;
|
|
}
|
|
if (demangleNatural(natural)) {
|
|
if (!Mangled.nextIf('_'))
|
|
return false;
|
|
natural++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Demangle an <index> and package it as a node of some kind.
|
|
NodePointer demangleIndexAsNode(Node::Kind kind = Node::Kind::Number) {
|
|
Node::IndexType index;
|
|
if (!demangleIndex(index))
|
|
return nullptr;
|
|
return NodeFactory::create(kind, index);
|
|
}
|
|
|
|
NodePointer createSwiftType(Node::Kind typeKind, StringRef name) {
|
|
NodePointer type = NodeFactory::create(typeKind);
|
|
type->addChild(NodeFactory::create(Node::Kind::Module, STDLIB_NAME));
|
|
type->addChild(NodeFactory::create(Node::Kind::Identifier, name));
|
|
return type;
|
|
}
|
|
|
|
/// Demangle a <substitution>, given that we've already consumed the 'S'.
|
|
NodePointer demangleSubstitutionIndex() {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
if (Mangled.nextIf('o'))
|
|
return NodeFactory::create(Node::Kind::Module, MANGLING_MODULE_OBJC);
|
|
if (Mangled.nextIf('C'))
|
|
return NodeFactory::create(Node::Kind::Module, MANGLING_MODULE_C);
|
|
if (Mangled.nextIf('a'))
|
|
return createSwiftType(Node::Kind::Structure, "Array");
|
|
if (Mangled.nextIf('b'))
|
|
return createSwiftType(Node::Kind::Structure, "Bool");
|
|
if (Mangled.nextIf('c'))
|
|
return createSwiftType(Node::Kind::Structure, "UnicodeScalar");
|
|
if (Mangled.nextIf('d'))
|
|
return createSwiftType(Node::Kind::Structure, "Double");
|
|
if (Mangled.nextIf('f'))
|
|
return createSwiftType(Node::Kind::Structure, "Float");
|
|
if (Mangled.nextIf('i'))
|
|
return createSwiftType(Node::Kind::Structure, "Int");
|
|
if (Mangled.nextIf('P'))
|
|
return createSwiftType(Node::Kind::Structure, "UnsafePointer");
|
|
if (Mangled.nextIf('p'))
|
|
return createSwiftType(Node::Kind::Structure, "UnsafeMutablePointer");
|
|
if (Mangled.nextIf('q'))
|
|
return createSwiftType(Node::Kind::Enum, "Optional");
|
|
if (Mangled.nextIf('Q'))
|
|
return createSwiftType(Node::Kind::Enum, "ImplicitlyUnwrappedOptional");
|
|
if (Mangled.nextIf('R'))
|
|
return createSwiftType(Node::Kind::Structure, "UnsafeBufferPointer");
|
|
if (Mangled.nextIf('r'))
|
|
return createSwiftType(Node::Kind::Structure, "UnsafeMutableBufferPointer");
|
|
if (Mangled.nextIf('S'))
|
|
return createSwiftType(Node::Kind::Structure, "String");
|
|
if (Mangled.nextIf('u'))
|
|
return createSwiftType(Node::Kind::Structure, "UInt");
|
|
Node::IndexType index_sub;
|
|
if (!demangleIndex(index_sub))
|
|
return nullptr;
|
|
if (index_sub >= Substitutions.size())
|
|
return nullptr;
|
|
return Substitutions[index_sub];
|
|
}
|
|
|
|
NodePointer demangleModule() {
|
|
if (Mangled.nextIf('s')) {
|
|
return NodeFactory::create(Node::Kind::Module, STDLIB_NAME);
|
|
}
|
|
if (Mangled.nextIf('S')) {
|
|
NodePointer module = demangleSubstitutionIndex();
|
|
if (!module)
|
|
return nullptr;
|
|
if (module->getKind() != Node::Kind::Module)
|
|
return nullptr;
|
|
return module;
|
|
}
|
|
|
|
NodePointer module = demangleIdentifier(Node::Kind::Module);
|
|
if (!module) return nullptr;
|
|
Substitutions.push_back(module);
|
|
return module;
|
|
}
|
|
|
|
NodePointer demangleDeclarationName(Node::Kind kind) {
|
|
NodePointer context = demangleContext();
|
|
if (!context) return nullptr;
|
|
|
|
auto name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
|
|
auto decl = NodeFactory::create(kind);
|
|
decl->addChild(context);
|
|
decl->addChild(name);
|
|
Substitutions.push_back(decl);
|
|
return decl;
|
|
}
|
|
|
|
NodePointer demangleProtocolName() {
|
|
NodePointer proto = demangleProtocolNameImpl();
|
|
if (!proto) return nullptr;
|
|
|
|
NodePointer type = NodeFactory::create(Node::Kind::Type);
|
|
type->addChild(proto);
|
|
return type;
|
|
}
|
|
|
|
NodePointer demangleProtocolNameGivenContext(NodePointer context) {
|
|
NodePointer name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
|
|
auto proto = NodeFactory::create(Node::Kind::Protocol);
|
|
proto->addChild(std::move(context));
|
|
proto->addChild(std::move(name));
|
|
Substitutions.push_back(proto);
|
|
return proto;
|
|
}
|
|
|
|
NodePointer demangleProtocolNameImpl() {
|
|
// There's an ambiguity in <protocol> between a substitution of
|
|
// the protocol and a substitution of the protocol's context, so
|
|
// we have to duplicate some of the logic from
|
|
// demangleDeclarationName.
|
|
if (Mangled.nextIf('S')) {
|
|
NodePointer sub = demangleSubstitutionIndex();
|
|
if (!sub) return nullptr;
|
|
if (sub->getKind() == Node::Kind::Protocol)
|
|
return sub;
|
|
|
|
if (sub->getKind() != Node::Kind::Module)
|
|
return nullptr;
|
|
|
|
return demangleProtocolNameGivenContext(sub);
|
|
}
|
|
|
|
if (Mangled.nextIf('s')) {
|
|
NodePointer stdlib = NodeFactory::create(Node::Kind::Module, STDLIB_NAME);
|
|
|
|
return demangleProtocolNameGivenContext(stdlib);
|
|
}
|
|
|
|
return demangleDeclarationName(Node::Kind::Protocol);
|
|
}
|
|
|
|
NodePointer demangleNominalType() {
|
|
if (Mangled.nextIf('S'))
|
|
return demangleSubstitutionIndex();
|
|
if (Mangled.nextIf('V'))
|
|
return demangleDeclarationName(Node::Kind::Structure);
|
|
if (Mangled.nextIf('O'))
|
|
return demangleDeclarationName(Node::Kind::Enum);
|
|
if (Mangled.nextIf('C'))
|
|
return demangleDeclarationName(Node::Kind::Class);
|
|
if (Mangled.nextIf('P'))
|
|
return demangleDeclarationName(Node::Kind::Protocol);
|
|
return nullptr;
|
|
}
|
|
|
|
NodePointer demangleContext() {
|
|
// context ::= module
|
|
// context ::= entity
|
|
// context ::= 'E' module context (extension defined in a different module)
|
|
// context ::= 'e' module context generic-signature (constrained extension)
|
|
if (!Mangled) return nullptr;
|
|
if (Mangled.nextIf('E')) {
|
|
NodePointer ext = NodeFactory::create(Node::Kind::Extension);
|
|
NodePointer def_module = demangleModule();
|
|
if (!def_module) return nullptr;
|
|
NodePointer type = demangleContext();
|
|
if (!type) return nullptr;
|
|
ext->addChild(def_module);
|
|
ext->addChild(type);
|
|
return ext;
|
|
}
|
|
if (Mangled.nextIf('e')) {
|
|
NodePointer ext = NodeFactory::create(Node::Kind::Extension);
|
|
NodePointer def_module = demangleModule();
|
|
if (!def_module) return nullptr;
|
|
NodePointer sig = demangleGenericSignature();
|
|
// The generic context is currently re-specified by the type mangling.
|
|
// If we ever remove 'self' from manglings, we should stop resetting the
|
|
// context here.
|
|
if (!sig) return nullptr;
|
|
NodePointer type = demangleContext();
|
|
if (!type) return nullptr;
|
|
|
|
ext->addChild(def_module);
|
|
ext->addChild(type);
|
|
ext->addChild(sig);
|
|
return ext;
|
|
}
|
|
if (Mangled.nextIf('S'))
|
|
return demangleSubstitutionIndex();
|
|
if (Mangled.nextIf('s'))
|
|
return NodeFactory::create(Node::Kind::Module, STDLIB_NAME);
|
|
if (isStartOfEntity(Mangled.peek()))
|
|
return demangleEntity();
|
|
return demangleModule();
|
|
}
|
|
|
|
NodePointer demangleProtocolList() {
|
|
NodePointer proto_list = NodeFactory::create(Node::Kind::ProtocolList);
|
|
NodePointer type_list = NodeFactory::create(Node::Kind::TypeList);
|
|
proto_list->addChild(type_list);
|
|
while (!Mangled.nextIf('_')) {
|
|
NodePointer proto = demangleProtocolName();
|
|
if (!proto)
|
|
return nullptr;
|
|
type_list->addChild(std::move(proto));
|
|
}
|
|
return proto_list;
|
|
}
|
|
|
|
NodePointer demangleProtocolConformance() {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer protocol = demangleProtocolName();
|
|
if (!protocol)
|
|
return nullptr;
|
|
NodePointer context = demangleContext();
|
|
if (!context)
|
|
return nullptr;
|
|
NodePointer proto_conformance =
|
|
NodeFactory::create(Node::Kind::ProtocolConformance);
|
|
proto_conformance->addChild(type);
|
|
proto_conformance->addChild(protocol);
|
|
proto_conformance->addChild(context);
|
|
return proto_conformance;
|
|
}
|
|
|
|
// entity ::= entity-kind context entity-name
|
|
// entity ::= nominal-type
|
|
NodePointer demangleEntity() {
|
|
// static?
|
|
bool isStatic = Mangled.nextIf('Z');
|
|
|
|
// entity-kind
|
|
Node::Kind entityBasicKind;
|
|
if (Mangled.nextIf('F')) {
|
|
entityBasicKind = Node::Kind::Function;
|
|
} else if (Mangled.nextIf('v')) {
|
|
entityBasicKind = Node::Kind::Variable;
|
|
} else if (Mangled.nextIf('I')) {
|
|
entityBasicKind = Node::Kind::Initializer;
|
|
} else if (Mangled.nextIf('i')) {
|
|
entityBasicKind = Node::Kind::Subscript;
|
|
} else {
|
|
return demangleNominalType();
|
|
}
|
|
|
|
NodePointer context = demangleContext();
|
|
if (!context) return nullptr;
|
|
|
|
// entity-name
|
|
Node::Kind entityKind;
|
|
bool hasType = true;
|
|
NodePointer name;
|
|
if (Mangled.nextIf('D')) {
|
|
entityKind = Node::Kind::Deallocator;
|
|
hasType = false;
|
|
} else if (Mangled.nextIf('d')) {
|
|
entityKind = Node::Kind::Destructor;
|
|
hasType = false;
|
|
} else if (Mangled.nextIf('e')) {
|
|
entityKind = Node::Kind::IVarInitializer;
|
|
hasType = false;
|
|
} else if (Mangled.nextIf('E')) {
|
|
entityKind = Node::Kind::IVarDestroyer;
|
|
hasType = false;
|
|
} else if (Mangled.nextIf('C')) {
|
|
entityKind = Node::Kind::Allocator;
|
|
} else if (Mangled.nextIf('c')) {
|
|
entityKind = Node::Kind::Constructor;
|
|
} else if (Mangled.nextIf('a')) {
|
|
if (Mangled.nextIf('O')) {
|
|
entityKind = Node::Kind::OwningMutableAddressor;
|
|
} else if (Mangled.nextIf('o')) {
|
|
entityKind = Node::Kind::NativeOwningMutableAddressor;
|
|
} else if (Mangled.nextIf('p')) {
|
|
entityKind = Node::Kind::NativePinningMutableAddressor;
|
|
} else if (Mangled.nextIf('u')) {
|
|
entityKind = Node::Kind::UnsafeMutableAddressor;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('l')) {
|
|
if (Mangled.nextIf('O')) {
|
|
entityKind = Node::Kind::OwningAddressor;
|
|
} else if (Mangled.nextIf('o')) {
|
|
entityKind = Node::Kind::NativeOwningAddressor;
|
|
} else if (Mangled.nextIf('p')) {
|
|
entityKind = Node::Kind::NativePinningAddressor;
|
|
} else if (Mangled.nextIf('u')) {
|
|
entityKind = Node::Kind::UnsafeAddressor;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('g')) {
|
|
entityKind = Node::Kind::Getter;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('G')) {
|
|
entityKind = Node::Kind::GlobalGetter;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('s')) {
|
|
entityKind = Node::Kind::Setter;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('m')) {
|
|
entityKind = Node::Kind::MaterializeForSet;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('w')) {
|
|
entityKind = Node::Kind::WillSet;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('W')) {
|
|
entityKind = Node::Kind::DidSet;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('U')) {
|
|
entityKind = Node::Kind::ExplicitClosure;
|
|
name = demangleIndexAsNode();
|
|
if (!name) return nullptr;
|
|
} else if (Mangled.nextIf('u')) {
|
|
entityKind = Node::Kind::ImplicitClosure;
|
|
name = demangleIndexAsNode();
|
|
if (!name) return nullptr;
|
|
} else if (entityBasicKind == Node::Kind::Initializer) {
|
|
// entity-name ::= 'A' index
|
|
if (Mangled.nextIf('A')) {
|
|
entityKind = Node::Kind::DefaultArgumentInitializer;
|
|
name = demangleIndexAsNode();
|
|
if (!name) return nullptr;
|
|
// entity-name ::= 'i'
|
|
} else if (Mangled.nextIf('i')) {
|
|
entityKind = Node::Kind::Initializer;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
hasType = false;
|
|
} else {
|
|
entityKind = entityBasicKind;
|
|
name = demangleDeclName();
|
|
if (!name) return nullptr;
|
|
}
|
|
|
|
NodePointer entity = NodeFactory::create(entityKind);
|
|
entity->addChild(context);
|
|
|
|
if (name) entity->addChild(name);
|
|
|
|
if (hasType) {
|
|
auto type = demangleType();
|
|
if (!type) return nullptr;
|
|
entity->addChild(type);
|
|
}
|
|
|
|
if (isStatic) {
|
|
auto staticNode = NodeFactory::create(Node::Kind::Static);
|
|
staticNode->addChild(entity);
|
|
return staticNode;
|
|
}
|
|
|
|
return entity;
|
|
}
|
|
|
|
NodePointer demangleArchetypeRef(Node::IndexType depth, Node::IndexType i) {
|
|
// FIXME: Name won't match demangled context generic signatures correctly.
|
|
auto ref = NodeFactory::create(Node::Kind::ArchetypeRef,
|
|
archetypeName(i, depth));
|
|
ref->addChild(NodeFactory::create(Node::Kind::Index, depth));
|
|
ref->addChild(NodeFactory::create(Node::Kind::Index, i));
|
|
return ref;
|
|
}
|
|
|
|
NodePointer getDependentGenericParamType(unsigned depth, unsigned index) {
|
|
std::string Name;
|
|
DemanglerPrinter PrintName(Name);
|
|
PrintName << archetypeName(index, depth);
|
|
|
|
auto paramTy = NodeFactory::create(Node::Kind::DependentGenericParamType,
|
|
std::move(Name));
|
|
paramTy->addChild(NodeFactory::create(Node::Kind::Index, depth));
|
|
paramTy->addChild(NodeFactory::create(Node::Kind::Index, index));
|
|
|
|
return paramTy;
|
|
}
|
|
|
|
NodePointer demangleGenericParamIndex() {
|
|
Node::IndexType depth, index;
|
|
|
|
if (Mangled.nextIf('d')) {
|
|
if (!demangleIndex(depth))
|
|
return nullptr;
|
|
depth += 1;
|
|
if (!demangleIndex(index))
|
|
return nullptr;
|
|
} else if (Mangled.nextIf('x')) {
|
|
depth = 0;
|
|
index = 0;
|
|
} else {
|
|
if (!demangleIndex(index))
|
|
return nullptr;
|
|
depth = 0;
|
|
index += 1;
|
|
}
|
|
return getDependentGenericParamType(depth, index);
|
|
}
|
|
|
|
NodePointer demangleDependentMemberTypeName(NodePointer base) {
|
|
assert(base->getKind() == Node::Kind::Type
|
|
&& "base should be a type");
|
|
NodePointer assocTy;
|
|
|
|
if (Mangled.nextIf('S')) {
|
|
assocTy = demangleSubstitutionIndex();
|
|
if (assocTy->getKind() != Node::Kind::DependentAssociatedTypeRef)
|
|
return nullptr;
|
|
} else {
|
|
NodePointer protocol = nullptr;
|
|
if (Mangled.nextIf('P')) {
|
|
protocol = demangleProtocolName();
|
|
if (!protocol) return nullptr;
|
|
}
|
|
|
|
// TODO: If the protocol name was elided from the assoc type mangling,
|
|
// we could try to fish it out of the generic signature constraints on the
|
|
// base.
|
|
assocTy = demangleIdentifier(Node::Kind::DependentAssociatedTypeRef);
|
|
if (!assocTy) return nullptr;
|
|
if (protocol)
|
|
assocTy->addChild(protocol);
|
|
|
|
Substitutions.push_back(assocTy);
|
|
}
|
|
|
|
NodePointer depTy = NodeFactory::create(Node::Kind::DependentMemberType);
|
|
depTy->addChild(base);
|
|
depTy->addChild(assocTy);
|
|
return depTy;
|
|
}
|
|
|
|
NodePointer demangleAssociatedTypeSimple() {
|
|
// Demangle the base type.
|
|
auto base = demangleGenericParamIndex();
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
NodePointer nodeType = NodeFactory::create(Node::Kind::Type);
|
|
nodeType->addChild(base);
|
|
|
|
// Demangle the associated type name.
|
|
return demangleDependentMemberTypeName(nodeType);
|
|
}
|
|
|
|
NodePointer demangleAssociatedTypeCompound() {
|
|
// Demangle the base type.
|
|
auto base = demangleGenericParamIndex();
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
// Demangle the associated type chain.
|
|
while (!Mangled.nextIf('_')) {
|
|
NodePointer nodeType = NodeFactory::create(Node::Kind::Type);
|
|
nodeType->addChild(base);
|
|
|
|
base = demangleDependentMemberTypeName(nodeType);
|
|
if (!base)
|
|
return nullptr;
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
NodePointer demangleDependentType() {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
|
|
// A dependent member type begins with a non-index, non-'d' character.
|
|
auto c = Mangled.peek();
|
|
if (c != 'd' && c != '_' && !isDigit(c)) {
|
|
NodePointer baseType = demangleType();
|
|
if (!baseType) return nullptr;
|
|
return demangleDependentMemberTypeName(baseType);
|
|
}
|
|
|
|
// Otherwise, we have a generic parameter.
|
|
return demangleGenericParamIndex();
|
|
}
|
|
|
|
NodePointer demangleConstrainedTypeImpl() {
|
|
// The constrained type can only be a generic parameter or an associated
|
|
// type thereof. The 'q' introducer is thus left off of generic params.
|
|
if (Mangled.nextIf('w')) {
|
|
return demangleAssociatedTypeSimple();
|
|
}
|
|
if (Mangled.nextIf('W')) {
|
|
return demangleAssociatedTypeCompound();
|
|
}
|
|
return demangleGenericParamIndex();
|
|
}
|
|
|
|
NodePointer demangleConstrainedType() {
|
|
auto type = demangleConstrainedTypeImpl();
|
|
if (!type)
|
|
return nullptr;
|
|
|
|
NodePointer nodeType = NodeFactory::create(Node::Kind::Type);
|
|
nodeType->addChild(type);
|
|
return nodeType;
|
|
}
|
|
|
|
NodePointer demangleGenericSignature() {
|
|
auto sig = NodeFactory::create(Node::Kind::DependentGenericSignature);
|
|
// First read in the parameter counts at each depth.
|
|
Node::IndexType count = ~(Node::IndexType)0;
|
|
|
|
auto addCount = [&]{
|
|
auto countNode =
|
|
NodeFactory::create(Node::Kind::DependentGenericParamCount, count);
|
|
sig->addChild(countNode);
|
|
};
|
|
|
|
while (Mangled.peek() != 'R' && Mangled.peek() != 'r') {
|
|
if (Mangled.nextIf('z')) {
|
|
count = 0;
|
|
} else if (demangleIndex(count)) {
|
|
count += 1;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
addCount();
|
|
}
|
|
|
|
// No mangled parameters means we have exactly one.
|
|
if (count == ~(Node::IndexType)0) {
|
|
count = 1;
|
|
addCount();
|
|
}
|
|
|
|
// Next read in the generic requirements, if any.
|
|
if (Mangled.nextIf('r'))
|
|
return sig;
|
|
|
|
if (!Mangled.nextIf('R'))
|
|
return nullptr;
|
|
|
|
while (!Mangled.nextIf('r')) {
|
|
NodePointer reqt = demangleGenericRequirement();
|
|
if (!reqt) return nullptr;
|
|
sig->addChild(reqt);
|
|
}
|
|
|
|
return sig;
|
|
}
|
|
|
|
NodePointer demangleMetatypeRepresentation() {
|
|
if (Mangled.nextIf('t'))
|
|
return NodeFactory::create(Node::Kind::MetatypeRepresentation, "@thin");
|
|
|
|
if (Mangled.nextIf('T'))
|
|
return NodeFactory::create(Node::Kind::MetatypeRepresentation, "@thick");
|
|
|
|
if (Mangled.nextIf('o'))
|
|
return NodeFactory::create(Node::Kind::MetatypeRepresentation,
|
|
"@objc_metatype");
|
|
|
|
unreachable("Unhandled metatype representation");
|
|
}
|
|
|
|
NodePointer demangleGenericRequirement() {
|
|
NodePointer constrainedType = demangleConstrainedType();
|
|
if (!constrainedType)
|
|
return nullptr;
|
|
if (Mangled.nextIf('z')) {
|
|
NodePointer second = demangleType();
|
|
if (!second) return nullptr;
|
|
auto reqt = NodeFactory::create(
|
|
Node::Kind::DependentGenericSameTypeRequirement);
|
|
reqt->addChild(constrainedType);
|
|
reqt->addChild(second);
|
|
return reqt;
|
|
}
|
|
|
|
// Base class constraints are introduced by a class type mangling, which
|
|
// will begin with either 'C' or 'S'.
|
|
if (!Mangled)
|
|
return nullptr;
|
|
NodePointer constraint;
|
|
|
|
auto next = Mangled.peek();
|
|
|
|
if (next == 'C') {
|
|
constraint = demangleType();
|
|
if (!constraint) return nullptr;
|
|
} else if (next == 'S') {
|
|
// A substitution may be either the module name of a protocol or a full
|
|
// type name.
|
|
NodePointer typeName;
|
|
Mangled.next();
|
|
NodePointer sub = demangleSubstitutionIndex();
|
|
if (!sub) return nullptr;
|
|
if (sub->getKind() == Node::Kind::Protocol
|
|
|| sub->getKind() == Node::Kind::Class) {
|
|
typeName = sub;
|
|
} else if (sub->getKind() == Node::Kind::Module) {
|
|
typeName = demangleProtocolNameGivenContext(sub);
|
|
if (!typeName)
|
|
return nullptr;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
constraint = NodeFactory::create(Node::Kind::Type);
|
|
constraint->addChild(typeName);
|
|
} else {
|
|
constraint = demangleProtocolName();
|
|
if (!constraint)
|
|
return nullptr;
|
|
}
|
|
auto reqt = NodeFactory::create(
|
|
Node::Kind::DependentGenericConformanceRequirement);
|
|
reqt->addChild(constrainedType);
|
|
reqt->addChild(constraint);
|
|
return reqt;
|
|
}
|
|
|
|
NodePointer demangleArchetypeType() {
|
|
auto makeSelfType = [&](NodePointer proto) -> NodePointer {
|
|
auto selfType = NodeFactory::create(Node::Kind::SelfTypeRef);
|
|
selfType->addChild(proto);
|
|
Substitutions.push_back(selfType);
|
|
return selfType;
|
|
};
|
|
|
|
auto makeAssociatedType = [&](NodePointer root) -> NodePointer {
|
|
NodePointer name = demangleIdentifier();
|
|
if (!name) return nullptr;
|
|
auto assocType = NodeFactory::create(Node::Kind::AssociatedTypeRef);
|
|
assocType->addChild(root);
|
|
assocType->addChild(name);
|
|
Substitutions.push_back(assocType);
|
|
return assocType;
|
|
};
|
|
|
|
if (Mangled.nextIf('P')) {
|
|
NodePointer proto = demangleProtocolName();
|
|
if (!proto) return nullptr;
|
|
return makeSelfType(proto);
|
|
}
|
|
|
|
if (Mangled.nextIf('Q')) {
|
|
NodePointer root = demangleArchetypeType();
|
|
if (!root) return nullptr;
|
|
return makeAssociatedType(root);
|
|
}
|
|
if (Mangled.nextIf('S')) {
|
|
NodePointer sub = demangleSubstitutionIndex();
|
|
if (!sub) return nullptr;
|
|
if (sub->getKind() == Node::Kind::Protocol)
|
|
return makeSelfType(sub);
|
|
else
|
|
return makeAssociatedType(sub);
|
|
}
|
|
if (Mangled.nextIf('s')) {
|
|
NodePointer stdlib = NodeFactory::create(Node::Kind::Module, STDLIB_NAME);
|
|
return makeAssociatedType(stdlib);
|
|
}
|
|
if (Mangled.nextIf('d')) {
|
|
Node::IndexType depth, index;
|
|
if (!demangleIndex(depth))
|
|
return nullptr;
|
|
if (!demangleIndex(index))
|
|
return nullptr;
|
|
return demangleArchetypeRef(depth + 1, index);
|
|
}
|
|
if (Mangled.nextIf('q')) {
|
|
NodePointer index = demangleIndexAsNode();
|
|
if (!index)
|
|
return nullptr;
|
|
NodePointer decl_ctx = NodeFactory::create(Node::Kind::DeclContext);
|
|
NodePointer ctx = demangleContext();
|
|
if (!ctx)
|
|
return nullptr;
|
|
decl_ctx->addChild(ctx);
|
|
auto qual_atype = NodeFactory::create(Node::Kind::QualifiedArchetype);
|
|
qual_atype->addChild(index);
|
|
qual_atype->addChild(decl_ctx);
|
|
return qual_atype;
|
|
}
|
|
Node::IndexType index;
|
|
if (!demangleIndex(index))
|
|
return nullptr;
|
|
return demangleArchetypeRef(0, index);
|
|
}
|
|
|
|
NodePointer demangleTuple(IsVariadic isV) {
|
|
NodePointer tuple = NodeFactory::create(
|
|
isV == IsVariadic::yes ? Node::Kind::VariadicTuple
|
|
: Node::Kind::NonVariadicTuple);
|
|
while (!Mangled.nextIf('_')) {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
NodePointer elt = NodeFactory::create(Node::Kind::TupleElement);
|
|
|
|
if (isStartOfIdentifier(Mangled.peek())) {
|
|
NodePointer label = demangleIdentifier(Node::Kind::TupleElementName);
|
|
if (!label)
|
|
return nullptr;
|
|
elt->addChild(label);
|
|
}
|
|
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
elt->addChild(type);
|
|
|
|
tuple->addChild(elt);
|
|
}
|
|
return tuple;
|
|
}
|
|
|
|
NodePointer postProcessReturnTypeNode (NodePointer out_args) {
|
|
NodePointer out_node = NodeFactory::create(Node::Kind::ReturnType);
|
|
out_node->addChild(out_args);
|
|
return out_node;
|
|
}
|
|
|
|
NodePointer demangleType() {
|
|
NodePointer type = demangleTypeImpl();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer nodeType = NodeFactory::create(Node::Kind::Type);
|
|
nodeType->addChild(type);
|
|
return nodeType;
|
|
}
|
|
|
|
NodePointer demangleFunctionType(Node::Kind kind) {
|
|
bool throws = false;
|
|
if (Mangled &&
|
|
Mangled.nextIf('z')) {
|
|
throws = true;
|
|
}
|
|
NodePointer in_args = demangleType();
|
|
if (!in_args)
|
|
return nullptr;
|
|
NodePointer out_args = demangleType();
|
|
if (!out_args)
|
|
return nullptr;
|
|
NodePointer block = NodeFactory::create(kind);
|
|
|
|
if (throws) {
|
|
block->addChild(NodeFactory::create(Node::Kind::ThrowsAnnotation));
|
|
}
|
|
|
|
NodePointer in_node = NodeFactory::create(Node::Kind::ArgumentTuple);
|
|
block->addChild(in_node);
|
|
in_node->addChild(in_args);
|
|
block->addChild(postProcessReturnTypeNode(out_args));
|
|
return block;
|
|
}
|
|
|
|
NodePointer demangleTypeImpl() {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
char c = Mangled.next();
|
|
if (c == 'B') {
|
|
if (!Mangled)
|
|
return nullptr;
|
|
c = Mangled.next();
|
|
if (c == 'b')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.BridgeObject");
|
|
if (c == 'B')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.UnsafeValueBuffer");
|
|
if (c == 'f') {
|
|
Node::IndexType size;
|
|
if (demangleBuiltinSize(size)) {
|
|
return NodeFactory::create(
|
|
Node::Kind::BuiltinTypeName,
|
|
(DemanglerPrinter("") << "Builtin.Float" << size).str());
|
|
}
|
|
}
|
|
if (c == 'i') {
|
|
Node::IndexType size;
|
|
if (demangleBuiltinSize(size)) {
|
|
return NodeFactory::create(
|
|
Node::Kind::BuiltinTypeName,
|
|
(DemanglerPrinter("") << "Builtin.Int" << size).str());
|
|
}
|
|
}
|
|
if (c == 'v') {
|
|
Node::IndexType elts;
|
|
if (demangleNatural(elts)) {
|
|
if (!Mangled.nextIf('B'))
|
|
return nullptr;
|
|
if (Mangled.nextIf('i')) {
|
|
Node::IndexType size;
|
|
if (!demangleBuiltinSize(size))
|
|
return nullptr;
|
|
return NodeFactory::create(
|
|
Node::Kind::BuiltinTypeName,
|
|
(DemanglerPrinter("") << "Builtin.Vec" << elts << "xInt" << size)
|
|
.str());
|
|
}
|
|
if (Mangled.nextIf('f')) {
|
|
Node::IndexType size;
|
|
if (!demangleBuiltinSize(size))
|
|
return nullptr;
|
|
return NodeFactory::create(
|
|
Node::Kind::BuiltinTypeName,
|
|
(DemanglerPrinter("") << "Builtin.Vec" << elts << "xFloat"
|
|
<< size).str());
|
|
}
|
|
if (Mangled.nextIf('p'))
|
|
return NodeFactory::create(
|
|
Node::Kind::BuiltinTypeName,
|
|
(DemanglerPrinter("") << "Builtin.Vec" << elts << "xRawPointer")
|
|
.str());
|
|
}
|
|
}
|
|
if (c == 'O')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.UnknownObject");
|
|
if (c == 'o')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.NativeObject");
|
|
if (c == 'p')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.RawPointer");
|
|
if (c == 'w')
|
|
return NodeFactory::create(Node::Kind::BuiltinTypeName,
|
|
"Builtin.Word");
|
|
return nullptr;
|
|
}
|
|
if (c == 'a')
|
|
return demangleDeclarationName(Node::Kind::TypeAlias);
|
|
|
|
if (c == 'b') {
|
|
return demangleFunctionType(Node::Kind::ObjCBlock);
|
|
}
|
|
if (c == 'c') {
|
|
return demangleFunctionType(Node::Kind::CFunctionPointer);
|
|
}
|
|
if (c == 'D') {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
|
|
NodePointer dynamicSelf = NodeFactory::create(Node::Kind::DynamicSelf);
|
|
dynamicSelf->addChild(type);
|
|
return dynamicSelf;
|
|
}
|
|
if (c == 'E') {
|
|
if (!Mangled.nextIf('R'))
|
|
return nullptr;
|
|
if (!Mangled.nextIf('R'))
|
|
return nullptr;
|
|
return NodeFactory::create(Node::Kind::ErrorType, std::string());
|
|
}
|
|
if (c == 'F') {
|
|
return demangleFunctionType(Node::Kind::FunctionType);
|
|
}
|
|
if (c == 'f') {
|
|
return demangleFunctionType(Node::Kind::UncurriedFunctionType);
|
|
}
|
|
if (c == 'G') {
|
|
NodePointer unboundType = demangleType();
|
|
if (!unboundType)
|
|
return nullptr;
|
|
NodePointer type_list = NodeFactory::create(Node::Kind::TypeList);
|
|
while (!Mangled.nextIf('_')) {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
type_list->addChild(type);
|
|
if (Mangled.isEmpty())
|
|
return nullptr;
|
|
}
|
|
Node::Kind bound_type_kind;
|
|
switch (unboundType->getChild(0)->getKind()) { // look through Type node
|
|
case Node::Kind::Class:
|
|
bound_type_kind = Node::Kind::BoundGenericClass;
|
|
break;
|
|
case Node::Kind::Structure:
|
|
bound_type_kind = Node::Kind::BoundGenericStructure;
|
|
break;
|
|
case Node::Kind::Enum:
|
|
bound_type_kind = Node::Kind::BoundGenericEnum;
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
NodePointer type_application =
|
|
NodeFactory::create(bound_type_kind);
|
|
type_application->addChild(unboundType);
|
|
type_application->addChild(type_list);
|
|
return type_application;
|
|
}
|
|
if (c == 'X') {
|
|
if (Mangled.nextIf('b')) {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer boxType = NodeFactory::create(Node::Kind::SILBoxType);
|
|
boxType->addChild(type);
|
|
return boxType;
|
|
}
|
|
}
|
|
if (c == 'K') {
|
|
return demangleFunctionType(Node::Kind::AutoClosureType);
|
|
}
|
|
if (c == 'M') {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer metatype = NodeFactory::create(Node::Kind::Metatype);
|
|
metatype->addChild(type);
|
|
return metatype;
|
|
}
|
|
if (c == 'X') {
|
|
if (Mangled.nextIf('M')) {
|
|
NodePointer metatypeRepr = demangleMetatypeRepresentation();
|
|
if (!metatypeRepr) return nullptr;
|
|
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer metatype = NodeFactory::create(Node::Kind::Metatype);
|
|
metatype->addChild(metatypeRepr);
|
|
metatype->addChild(type);
|
|
return metatype;
|
|
}
|
|
}
|
|
if (c == 'P') {
|
|
if (Mangled.nextIf('M')) {
|
|
NodePointer type = demangleType();
|
|
if (!type) return nullptr;
|
|
auto metatype = NodeFactory::create(Node::Kind::ExistentialMetatype);
|
|
metatype->addChild(type);
|
|
return metatype;
|
|
}
|
|
|
|
return demangleProtocolList();
|
|
}
|
|
|
|
if (c == 'X') {
|
|
if (Mangled.nextIf('P')) {
|
|
if (Mangled.nextIf('M')) {
|
|
NodePointer metatypeRepr = demangleMetatypeRepresentation();
|
|
if (!metatypeRepr) return nullptr;
|
|
|
|
NodePointer type = demangleType();
|
|
if (!type) return nullptr;
|
|
|
|
auto metatype = NodeFactory::create(Node::Kind::ExistentialMetatype);
|
|
metatype->addChild(metatypeRepr);
|
|
metatype->addChild(type);
|
|
return metatype;
|
|
}
|
|
|
|
return demangleProtocolList();
|
|
}
|
|
}
|
|
if (c == 'Q') {
|
|
return demangleArchetypeType();
|
|
}
|
|
if (c == 'q') {
|
|
return demangleDependentType();
|
|
}
|
|
if (c == 'x') {
|
|
// Special mangling for the first generic param.
|
|
return getDependentGenericParamType(0, 0);
|
|
}
|
|
if (c == 'w') {
|
|
return demangleAssociatedTypeSimple();
|
|
}
|
|
if (c == 'W') {
|
|
return demangleAssociatedTypeCompound();
|
|
}
|
|
if (c == 'R') {
|
|
NodePointer inout = NodeFactory::create(Node::Kind::InOut);
|
|
NodePointer type = demangleTypeImpl();
|
|
if (!type)
|
|
return nullptr;
|
|
inout->addChild(type);
|
|
return inout;
|
|
}
|
|
if (c == 'S') {
|
|
return demangleSubstitutionIndex();
|
|
}
|
|
if (c == 'T') {
|
|
return demangleTuple(IsVariadic::no);
|
|
}
|
|
if (c == 't') {
|
|
return demangleTuple(IsVariadic::yes);
|
|
}
|
|
if (c == 'u') {
|
|
NodePointer sig = demangleGenericSignature();
|
|
if (!sig) return nullptr;
|
|
NodePointer sub = demangleType();
|
|
if (!sub) return nullptr;
|
|
NodePointer dependentGenericType
|
|
= NodeFactory::create(Node::Kind::DependentGenericType);
|
|
dependentGenericType->addChild(sig);
|
|
dependentGenericType->addChild(sub);
|
|
return dependentGenericType;
|
|
}
|
|
if (c == 'X') {
|
|
if (Mangled.nextIf('f')) {
|
|
return demangleFunctionType(Node::Kind::ThinFunctionType);
|
|
}
|
|
if (Mangled.nextIf('o')) {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer unowned = NodeFactory::create(Node::Kind::Unowned);
|
|
unowned->addChild(type);
|
|
return unowned;
|
|
}
|
|
if (Mangled.nextIf('u')) {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer unowned = NodeFactory::create(Node::Kind::Unmanaged);
|
|
unowned->addChild(type);
|
|
return unowned;
|
|
}
|
|
if (Mangled.nextIf('w')) {
|
|
NodePointer type = demangleType();
|
|
if (!type)
|
|
return nullptr;
|
|
NodePointer weak = NodeFactory::create(Node::Kind::Weak);
|
|
weak->addChild(type);
|
|
return weak;
|
|
}
|
|
|
|
// type ::= 'XF' impl-function-type
|
|
if (Mangled.nextIf('F')) {
|
|
return demangleImplFunctionType();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
if (isStartOfNominalType(c)) {
|
|
NodePointer nominal_type = demangleDeclarationName(nominalTypeMarkerToNodeKind(c));
|
|
return nominal_type;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool demangleReabstractSignature(NodePointer signature) {
|
|
if (Mangled.nextIf('G')) {
|
|
NodePointer generics = demangleGenericSignature();
|
|
if (!generics) return false;
|
|
signature->addChild(std::move(generics));
|
|
}
|
|
|
|
NodePointer srcType = demangleType();
|
|
if (!srcType) return false;
|
|
signature->addChild(std::move(srcType));
|
|
|
|
NodePointer destType = demangleType();
|
|
if (!destType) return false;
|
|
signature->addChild(std::move(destType));
|
|
|
|
return true;
|
|
}
|
|
|
|
// impl-function-type ::= impl-callee-convention impl-function-attribute*
|
|
// generics? '_' impl-parameter* '_' impl-result* '_'
|
|
// impl-function-attribute ::= 'Cb' // compatible with C block invocation function
|
|
// impl-function-attribute ::= 'Cc' // compatible with C global function
|
|
// impl-function-attribute ::= 'Cm' // compatible with Swift method
|
|
// impl-function-attribute ::= 'CO' // compatible with ObjC method
|
|
// impl-function-attribute ::= 'Cw' // compatible with protocol witness
|
|
// impl-function-attribute ::= 'N' // noreturn
|
|
// impl-function-attribute ::= 'G' // generic
|
|
NodePointer demangleImplFunctionType() {
|
|
NodePointer type = NodeFactory::create(Node::Kind::ImplFunctionType);
|
|
|
|
if (!demangleImplCalleeConvention(type))
|
|
return nullptr;
|
|
|
|
if (Mangled.nextIf('C')) {
|
|
if (Mangled.nextIf('b'))
|
|
addImplFunctionAttribute(type, "@convention(block)");
|
|
else if (Mangled.nextIf('c'))
|
|
addImplFunctionAttribute(type, "@convention(c)");
|
|
else if (Mangled.nextIf('m'))
|
|
addImplFunctionAttribute(type, "@convention(method)");
|
|
else if (Mangled.nextIf('O'))
|
|
addImplFunctionAttribute(type, "@convention(objc_method)");
|
|
else if (Mangled.nextIf('w'))
|
|
addImplFunctionAttribute(type, "@convention(witness_method)");
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
if (Mangled.nextIf('N'))
|
|
addImplFunctionAttribute(type, "@noreturn");
|
|
|
|
// Enter a new generic context if this type is generic.
|
|
// FIXME: replace with std::optional, when we have it.
|
|
if (Mangled.nextIf('G')) {
|
|
NodePointer generics = demangleGenericSignature();
|
|
if (!generics)
|
|
return nullptr;
|
|
type->addChild(generics);
|
|
}
|
|
|
|
// Expect the attribute terminator.
|
|
if (!Mangled.nextIf('_'))
|
|
return nullptr;
|
|
|
|
// Demangle the parameters.
|
|
if (!demangleImplParameters(type))
|
|
return nullptr;
|
|
|
|
// Demangle the result type.
|
|
if (!demangleImplResults(type))
|
|
return nullptr;
|
|
|
|
return type;
|
|
}
|
|
|
|
enum class ImplConventionContext { Callee, Parameter, Result };
|
|
|
|
/// impl-convention ::= 'a' // direct, autoreleased
|
|
/// impl-convention ::= 'd' // direct, no ownership transfer
|
|
/// impl-convention ::= 'D' // direct, no ownership transfer,
|
|
/// // dependent on self
|
|
/// impl-convention ::= 'g' // direct, guaranteed
|
|
/// impl-convention ::= 'e' // direct, deallocating
|
|
/// impl-convention ::= 'i' // indirect, ownership transfer
|
|
/// impl-convention ::= 'l' // indirect, inout
|
|
/// impl-convention ::= 'o' // direct, ownership transfer
|
|
///
|
|
/// Returns an empty string otherwise.
|
|
StringRef demangleImplConvention(ImplConventionContext ctxt) {
|
|
#define CASE(CHAR, FOR_CALLEE, FOR_PARAMETER, FOR_RESULT) \
|
|
if (Mangled.nextIf(CHAR)) { \
|
|
switch (ctxt) { \
|
|
case ImplConventionContext::Callee: return (FOR_CALLEE); \
|
|
case ImplConventionContext::Parameter: return (FOR_PARAMETER); \
|
|
case ImplConventionContext::Result: return (FOR_RESULT); \
|
|
} \
|
|
unreachable("bad context"); \
|
|
}
|
|
auto Nothing = StringRef();
|
|
CASE('a', Nothing, Nothing, "@autoreleased")
|
|
CASE('d', "@callee_unowned", "@unowned", "@unowned")
|
|
CASE('d', Nothing, Nothing, "@unowned_inner_pointer")
|
|
CASE('g', "@callee_guaranteed", "@guaranteed", Nothing)
|
|
CASE('e', Nothing, "@deallocating", Nothing)
|
|
CASE('i', Nothing, "@in", "@out")
|
|
CASE('l', Nothing, "@inout", Nothing)
|
|
CASE('o', "@callee_owned", "@owned", "@owned")
|
|
return Nothing;
|
|
#undef CASE
|
|
}
|
|
|
|
// impl-callee-convention ::= 't'
|
|
// impl-callee-convention ::= impl-convention
|
|
bool demangleImplCalleeConvention(NodePointer type) {
|
|
StringRef attr;
|
|
if (Mangled.nextIf('t')) {
|
|
attr = "@convention(thin)";
|
|
} else {
|
|
attr = demangleImplConvention(ImplConventionContext::Callee);
|
|
}
|
|
if (attr.empty()) {
|
|
return false;
|
|
}
|
|
type->addChild(NodeFactory::create(Node::Kind::ImplConvention, attr));
|
|
return true;
|
|
}
|
|
|
|
void addImplFunctionAttribute(NodePointer parent, StringRef attr,
|
|
Node::Kind kind = Node::Kind::ImplFunctionAttribute) {
|
|
parent->addChild(NodeFactory::create(kind, attr));
|
|
}
|
|
|
|
// impl-parameter ::= impl-convention type
|
|
bool demangleImplParameters(NodePointer parent) {
|
|
while (!Mangled.nextIf('_')) {
|
|
auto input = demangleImplParameterOrResult(Node::Kind::ImplParameter);
|
|
if (!input) return false;
|
|
parent->addChild(input);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// impl-result ::= impl-convention type
|
|
bool demangleImplResults(NodePointer parent) {
|
|
while (!Mangled.nextIf('_')) {
|
|
auto res = demangleImplParameterOrResult(Node::Kind::ImplResult);
|
|
if (!res) return false;
|
|
parent->addChild(res);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NodePointer demangleImplParameterOrResult(Node::Kind kind) {
|
|
if (Mangled.nextIf('z')) {
|
|
// Only valid for a result.
|
|
if (kind != Node::Kind::ImplResult)
|
|
return nullptr;
|
|
kind = Node::Kind::ImplErrorResult;
|
|
}
|
|
|
|
auto getContext = [](Node::Kind kind) -> ImplConventionContext {
|
|
if (kind == Node::Kind::ImplParameter)
|
|
return ImplConventionContext::Parameter;
|
|
else if (kind == Node::Kind::ImplResult
|
|
|| kind == Node::Kind::ImplErrorResult)
|
|
return ImplConventionContext::Result;
|
|
else
|
|
unreachable("unexpected node kind");
|
|
};
|
|
|
|
auto convention = demangleImplConvention(getContext(kind));
|
|
if (convention.empty()) return nullptr;
|
|
auto type = demangleType();
|
|
if (!type) return nullptr;
|
|
|
|
NodePointer node = NodeFactory::create(kind);
|
|
node->addChild(NodeFactory::create(Node::Kind::ImplConvention,
|
|
convention));
|
|
node->addChild(type);
|
|
|
|
return node;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
NodePointer
|
|
swift::Demangle::demangleSymbolAsNode(const char *MangledName,
|
|
size_t MangledNameLength,
|
|
const DemangleOptions &Options) {
|
|
Demangler demangler(StringRef(MangledName, MangledNameLength));
|
|
return demangler.demangleTopLevel();
|
|
}
|
|
|
|
NodePointer
|
|
swift::Demangle::demangleTypeAsNode(const char *MangledName,
|
|
size_t MangledNameLength,
|
|
const DemangleOptions &Options) {
|
|
Demangler demangler(StringRef(MangledName, MangledNameLength));
|
|
return demangler.demangleTypeName();
|
|
}
|
|
|
|
namespace {
|
|
class NodePrinter {
|
|
private:
|
|
std::string Str;
|
|
DemanglerPrinter Printer;
|
|
DemangleOptions Options;
|
|
|
|
public:
|
|
NodePrinter(DemangleOptions options) : Printer(Str), Options(options) {}
|
|
|
|
std::string printRoot(NodePointer root) {
|
|
print(root);
|
|
return Str;
|
|
}
|
|
|
|
private:
|
|
void printChildren(Node::iterator begin,
|
|
Node::iterator end,
|
|
const char *sep = nullptr) {
|
|
for (; begin != end;) {
|
|
print(*begin);
|
|
++begin;
|
|
if (sep && begin != end)
|
|
Printer << sep;
|
|
}
|
|
}
|
|
|
|
void printChildren(NodePointer pointer, const char *sep = nullptr) {
|
|
if (!pointer)
|
|
return;
|
|
Node::iterator begin = pointer->begin(), end = pointer->end();
|
|
printChildren(begin, end, sep);
|
|
}
|
|
|
|
NodePointer getFirstChildOfKind(NodePointer pointer, Node::Kind kind) {
|
|
if (!pointer)
|
|
return nullptr;
|
|
for (NodePointer &child : *pointer) {
|
|
if (child && child->getKind() == kind)
|
|
return child;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void printBoundGenericNoSugar(NodePointer pointer) {
|
|
if (pointer->getNumChildren() < 2)
|
|
return;
|
|
NodePointer typelist = pointer->getChild(1);
|
|
print(pointer->getChild(0));
|
|
Printer << "<";
|
|
printChildren(typelist, ", ");
|
|
Printer << ">";
|
|
}
|
|
|
|
static bool isSwiftModule(NodePointer node) {
|
|
return (node->getKind() == Node::Kind::Module &&
|
|
node->getText() == STDLIB_NAME);
|
|
}
|
|
|
|
static bool isDebuggerGeneratedModule(NodePointer node) {
|
|
return (node->getKind() == Node::Kind::Module &&
|
|
0 == node->getText().find(LLDB_EXPRESSIONS_MODULE_NAME_PREFIX));
|
|
}
|
|
|
|
static bool isIdentifier(NodePointer node, StringRef desired) {
|
|
return (node->getKind() == Node::Kind::Identifier &&
|
|
node->getText() == desired);
|
|
}
|
|
|
|
enum class SugarType {
|
|
None,
|
|
Optional,
|
|
ImplicitlyUnwrappedOptional,
|
|
Array,
|
|
Dictionary
|
|
};
|
|
|
|
/// Determine whether this is a "simple" type, from the type-simple
|
|
/// production.
|
|
bool isSimpleType(NodePointer pointer) {
|
|
switch (pointer->getKind()) {
|
|
case Node::Kind::Archetype:
|
|
case Node::Kind::ArchetypeRef:
|
|
case Node::Kind::AssociatedType:
|
|
case Node::Kind::AssociatedTypeRef:
|
|
case Node::Kind::BoundGenericClass:
|
|
case Node::Kind::BoundGenericEnum:
|
|
case Node::Kind::BoundGenericStructure:
|
|
case Node::Kind::BuiltinTypeName:
|
|
case Node::Kind::Class:
|
|
case Node::Kind::DependentGenericType:
|
|
case Node::Kind::DependentMemberType:
|
|
case Node::Kind::DependentGenericParamType:
|
|
case Node::Kind::DynamicSelf:
|
|
case Node::Kind::Enum:
|
|
case Node::Kind::ErrorType:
|
|
case Node::Kind::ExistentialMetatype:
|
|
case Node::Kind::Metatype:
|
|
case Node::Kind::MetatypeRepresentation:
|
|
case Node::Kind::Module:
|
|
case Node::Kind::NonVariadicTuple:
|
|
case Node::Kind::Protocol:
|
|
case Node::Kind::QualifiedArchetype:
|
|
case Node::Kind::ReturnType:
|
|
case Node::Kind::SelfTypeRef:
|
|
case Node::Kind::SILBoxType:
|
|
case Node::Kind::Structure:
|
|
case Node::Kind::TupleElementName:
|
|
case Node::Kind::Type:
|
|
case Node::Kind::TypeAlias:
|
|
case Node::Kind::TypeList:
|
|
case Node::Kind::VariadicTuple:
|
|
return true;
|
|
|
|
case Node::Kind::Allocator:
|
|
case Node::Kind::ArgumentTuple:
|
|
case Node::Kind::AssociatedTypeMetadataAccessor:
|
|
case Node::Kind::AssociatedTypeWitnessTableAccessor:
|
|
case Node::Kind::AutoClosureType:
|
|
case Node::Kind::CFunctionPointer:
|
|
case Node::Kind::Constructor:
|
|
case Node::Kind::Deallocator:
|
|
case Node::Kind::DeclContext:
|
|
case Node::Kind::DefaultArgumentInitializer:
|
|
case Node::Kind::DependentAssociatedTypeRef:
|
|
case Node::Kind::DependentGenericSignature:
|
|
case Node::Kind::DependentGenericParamCount:
|
|
case Node::Kind::DependentGenericConformanceRequirement:
|
|
case Node::Kind::DependentGenericSameTypeRequirement:
|
|
case Node::Kind::Destructor:
|
|
case Node::Kind::DidSet:
|
|
case Node::Kind::DirectMethodReferenceAttribute:
|
|
case Node::Kind::Directness:
|
|
case Node::Kind::DynamicAttribute:
|
|
case Node::Kind::ExplicitClosure:
|
|
case Node::Kind::Extension:
|
|
case Node::Kind::FieldOffset:
|
|
case Node::Kind::FullTypeMetadata:
|
|
case Node::Kind::Function:
|
|
case Node::Kind::FunctionSignatureSpecialization:
|
|
case Node::Kind::FunctionSignatureSpecializationParam:
|
|
case Node::Kind::FunctionSignatureSpecializationParamKind:
|
|
case Node::Kind::FunctionSignatureSpecializationParamPayload:
|
|
case Node::Kind::FunctionType:
|
|
case Node::Kind::Generics:
|
|
case Node::Kind::GenericProtocolWitnessTable:
|
|
case Node::Kind::GenericProtocolWitnessTableInstantiationFunction:
|
|
case Node::Kind::GenericSpecialization:
|
|
case Node::Kind::GenericSpecializationNotReAbstracted:
|
|
case Node::Kind::GenericSpecializationParam:
|
|
case Node::Kind::GenericType:
|
|
case Node::Kind::GenericTypeMetadataPattern:
|
|
case Node::Kind::Getter:
|
|
case Node::Kind::Global:
|
|
case Node::Kind::GlobalGetter:
|
|
case Node::Kind::Identifier:
|
|
case Node::Kind::Index:
|
|
case Node::Kind::IVarInitializer:
|
|
case Node::Kind::IVarDestroyer:
|
|
case Node::Kind::ImplConvention:
|
|
case Node::Kind::ImplFunctionAttribute:
|
|
case Node::Kind::ImplFunctionType:
|
|
case Node::Kind::ImplicitClosure:
|
|
case Node::Kind::ImplParameter:
|
|
case Node::Kind::ImplResult:
|
|
case Node::Kind::ImplErrorResult:
|
|
case Node::Kind::InOut:
|
|
case Node::Kind::InfixOperator:
|
|
case Node::Kind::Initializer:
|
|
case Node::Kind::LazyProtocolWitnessTableAccessor:
|
|
case Node::Kind::LazyProtocolWitnessTableCacheVariable:
|
|
case Node::Kind::LocalDeclName:
|
|
case Node::Kind::PrivateDeclName:
|
|
case Node::Kind::MaterializeForSet:
|
|
case Node::Kind::Metaclass:
|
|
case Node::Kind::NativeOwningAddressor:
|
|
case Node::Kind::NativeOwningMutableAddressor:
|
|
case Node::Kind::NativePinningAddressor:
|
|
case Node::Kind::NativePinningMutableAddressor:
|
|
case Node::Kind::NominalTypeDescriptor:
|
|
case Node::Kind::NonObjCAttribute:
|
|
case Node::Kind::Number:
|
|
case Node::Kind::ObjCAttribute:
|
|
case Node::Kind::ObjCBlock:
|
|
case Node::Kind::OwningAddressor:
|
|
case Node::Kind::OwningMutableAddressor:
|
|
case Node::Kind::PartialApplyForwarder:
|
|
case Node::Kind::PartialApplyObjCForwarder:
|
|
case Node::Kind::PostfixOperator:
|
|
case Node::Kind::PrefixOperator:
|
|
case Node::Kind::ProtocolConformance:
|
|
case Node::Kind::ProtocolDescriptor:
|
|
case Node::Kind::ProtocolList:
|
|
case Node::Kind::ProtocolWitness:
|
|
case Node::Kind::ProtocolWitnessTable:
|
|
case Node::Kind::ProtocolWitnessTableAccessor:
|
|
case Node::Kind::ReabstractionThunk:
|
|
case Node::Kind::ReabstractionThunkHelper:
|
|
case Node::Kind::Setter:
|
|
case Node::Kind::SpecializationPassID:
|
|
case Node::Kind::Static:
|
|
case Node::Kind::Subscript:
|
|
case Node::Kind::Suffix:
|
|
case Node::Kind::ThinFunctionType:
|
|
case Node::Kind::TupleElement:
|
|
case Node::Kind::TypeMangling:
|
|
case Node::Kind::TypeMetadata:
|
|
case Node::Kind::TypeMetadataAccessFunction:
|
|
case Node::Kind::TypeMetadataLazyCache:
|
|
case Node::Kind::UncurriedFunctionType:
|
|
case Node::Kind::Unmanaged:
|
|
case Node::Kind::Unowned:
|
|
case Node::Kind::UnsafeAddressor:
|
|
case Node::Kind::UnsafeMutableAddressor:
|
|
case Node::Kind::ValueWitness:
|
|
case Node::Kind::ValueWitnessTable:
|
|
case Node::Kind::Variable:
|
|
case Node::Kind::VTableAttribute:
|
|
case Node::Kind::Weak:
|
|
case Node::Kind::WillSet:
|
|
case Node::Kind::WitnessTableOffset:
|
|
case Node::Kind::ThrowsAnnotation:
|
|
return false;
|
|
}
|
|
unreachable("bad node kind");
|
|
}
|
|
|
|
SugarType findSugar(NodePointer pointer) {
|
|
if (pointer->getNumChildren() == 1 &&
|
|
pointer->getKind() == Node::Kind::Type)
|
|
return findSugar(pointer->getChild(0));
|
|
|
|
if (pointer->getNumChildren() != 2)
|
|
return SugarType::None;
|
|
|
|
if (pointer->getKind() != Node::Kind::BoundGenericEnum &&
|
|
pointer->getKind() != Node::Kind::BoundGenericStructure)
|
|
return SugarType::None;
|
|
|
|
auto unboundType = pointer->getChild(0)->getChild(0); // drill through Type
|
|
auto typeArgs = pointer->getChild(1);
|
|
|
|
if (pointer->getKind() == Node::Kind::BoundGenericEnum) {
|
|
// Swift.Optional
|
|
if (isIdentifier(unboundType->getChild(1), "Optional") &&
|
|
typeArgs->getNumChildren() == 1 &&
|
|
isSwiftModule(unboundType->getChild(0))) {
|
|
return SugarType::Optional;
|
|
}
|
|
|
|
// Swift.ImplicitlyUnwrappedOptional
|
|
if (isIdentifier(unboundType->getChild(1),
|
|
"ImplicitlyUnwrappedOptional") &&
|
|
typeArgs->getNumChildren() == 1 &&
|
|
isSwiftModule(unboundType->getChild(0))) {
|
|
return SugarType::ImplicitlyUnwrappedOptional;
|
|
}
|
|
|
|
return SugarType::None;
|
|
}
|
|
|
|
assert(pointer->getKind() == Node::Kind::BoundGenericStructure);
|
|
|
|
// Array
|
|
if (isIdentifier(unboundType->getChild(1), "Array") &&
|
|
typeArgs->getNumChildren() == 1 &&
|
|
isSwiftModule(unboundType->getChild(0))) {
|
|
return SugarType::Array;
|
|
}
|
|
|
|
// Dictionary
|
|
if (isIdentifier(unboundType->getChild(1), "Dictionary") &&
|
|
typeArgs->getNumChildren() == 2 &&
|
|
isSwiftModule(unboundType->getChild(0))) {
|
|
return SugarType::Dictionary;
|
|
}
|
|
|
|
return SugarType::None;
|
|
}
|
|
|
|
void printBoundGeneric(NodePointer pointer) {
|
|
if (pointer->getNumChildren() < 2)
|
|
return;
|
|
if (pointer->getNumChildren() != 2) {
|
|
printBoundGenericNoSugar(pointer);
|
|
return;
|
|
}
|
|
|
|
if (!Options.SynthesizeSugarOnTypes ||
|
|
pointer->getKind() == Node::Kind::BoundGenericClass)
|
|
{
|
|
// no sugar here
|
|
printBoundGenericNoSugar(pointer);
|
|
return;
|
|
}
|
|
|
|
SugarType sugarType = findSugar(pointer);
|
|
|
|
switch (sugarType) {
|
|
case SugarType::None:
|
|
printBoundGenericNoSugar(pointer);
|
|
break;
|
|
case SugarType::Optional:
|
|
case SugarType::ImplicitlyUnwrappedOptional: {
|
|
NodePointer type = pointer->getChild(1)->getChild(0);
|
|
bool needs_parens = !isSimpleType(type);
|
|
if (needs_parens)
|
|
Printer << "(";
|
|
print(type);
|
|
if (needs_parens)
|
|
Printer << ")";
|
|
Printer << (sugarType == SugarType::Optional ? "?" : "!");
|
|
break;
|
|
}
|
|
case SugarType::Array: {
|
|
NodePointer type = pointer->getChild(1)->getChild(0);
|
|
Printer << "[";
|
|
print(type);
|
|
Printer << "]";
|
|
break;
|
|
}
|
|
case SugarType::Dictionary: {
|
|
NodePointer keyType = pointer->getChild(1)->getChild(0);
|
|
NodePointer valueType = pointer->getChild(1)->getChild(1);
|
|
Printer << "[";
|
|
print(keyType);
|
|
Printer << " : ";
|
|
print(valueType);
|
|
Printer << "]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void printSimplifiedEntityType(NodePointer context, NodePointer entityType);
|
|
|
|
void printFunctionType(NodePointer node) {
|
|
assert(node->getNumChildren() == 2 || node->getNumChildren() == 3);
|
|
unsigned startIndex = 0;
|
|
bool throws = false;
|
|
if (node->getNumChildren() == 3) {
|
|
assert(node->getChild(0)->getKind() == Node::Kind::ThrowsAnnotation);
|
|
startIndex++;
|
|
throws = true;
|
|
}
|
|
print(node->getChild(startIndex));
|
|
if (throws) Printer << " throws";
|
|
print(node->getChild(startIndex+1));
|
|
}
|
|
|
|
void printImplFunctionType(NodePointer fn) {
|
|
enum State { Attrs, Inputs, Results } curState = Attrs;
|
|
auto transitionTo = [&](State newState) {
|
|
assert(newState >= curState);
|
|
for (; curState != newState; curState = State(curState + 1)) {
|
|
switch (curState) {
|
|
case Attrs: Printer << '('; continue;
|
|
case Inputs: Printer << ") -> ("; continue;
|
|
case Results: unreachable("no state after Results");
|
|
}
|
|
unreachable("bad state");
|
|
}
|
|
};
|
|
|
|
for (auto &child : *fn) {
|
|
if (child->getKind() == Node::Kind::ImplParameter) {
|
|
if (curState == Inputs) Printer << ", ";
|
|
transitionTo(Inputs);
|
|
print(child);
|
|
} else if (child->getKind() == Node::Kind::ImplResult
|
|
|| child->getKind() == Node::Kind::ImplErrorResult) {
|
|
if (curState == Results) Printer << ", ";
|
|
transitionTo(Results);
|
|
print(child);
|
|
} else {
|
|
assert(curState == Attrs);
|
|
print(child);
|
|
Printer << ' ';
|
|
}
|
|
}
|
|
transitionTo(Results);
|
|
Printer << ')';
|
|
}
|
|
|
|
void printContext(NodePointer context) {
|
|
// TODO: parenthesize local contexts?
|
|
if (Options.DisplayDebuggerGeneratedModule ||
|
|
!isDebuggerGeneratedModule(context))
|
|
{
|
|
print(context, /*asContext*/ true);
|
|
if (context->getKind() == Node::Kind::Module && !Options.DisplayModuleNames)
|
|
return;
|
|
Printer << '.';
|
|
}
|
|
}
|
|
|
|
void print(NodePointer pointer, bool asContext = false, bool suppressType = false);
|
|
|
|
unsigned printFunctionSigSpecializationParam(NodePointer pointer,
|
|
unsigned Idx);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static bool isExistentialType(NodePointer node) {
|
|
assert(node->getKind() == Node::Kind::Type);
|
|
node = node->getChild(0);
|
|
return (node->getKind() == Node::Kind::ExistentialMetatype ||
|
|
node->getKind() == Node::Kind::ProtocolList);
|
|
}
|
|
|
|
/// Print the relevant parameters and return the new index.
|
|
unsigned NodePrinter::printFunctionSigSpecializationParam(NodePointer pointer,
|
|
unsigned Idx) {
|
|
NodePointer firstChild = pointer->getChild(Idx);
|
|
unsigned V = firstChild->getIndex();
|
|
auto K = FunctionSigSpecializationParamKind(V);
|
|
switch (K) {
|
|
case FunctionSigSpecializationParamKind::BoxToValue:
|
|
case FunctionSigSpecializationParamKind::BoxToStack:
|
|
print(pointer->getChild(Idx++));
|
|
return Idx;
|
|
case FunctionSigSpecializationParamKind::ConstantPropFunction:
|
|
case FunctionSigSpecializationParamKind::ConstantPropGlobal: {
|
|
Printer << "[";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << " : ";
|
|
const auto &text = pointer->getChild(Idx++)->getText();
|
|
std::string demangledName = demangleSymbolAsString(text);
|
|
if (demangledName.empty()) {
|
|
Printer << text;
|
|
} else {
|
|
Printer << demangledName;
|
|
}
|
|
Printer << "]";
|
|
return Idx;
|
|
}
|
|
case FunctionSigSpecializationParamKind::ConstantPropInteger:
|
|
case FunctionSigSpecializationParamKind::ConstantPropFloat:
|
|
Printer << "[";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << " : ";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << "]";
|
|
return Idx;
|
|
case FunctionSigSpecializationParamKind::ConstantPropString:
|
|
Printer << "[";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << " : ";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << "'";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << "'";
|
|
Printer << "]";
|
|
return Idx;
|
|
case FunctionSigSpecializationParamKind::ClosureProp:
|
|
Printer << "[";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << " : ";
|
|
print(pointer->getChild(Idx++));
|
|
Printer << ", Argument Types : [";
|
|
for (unsigned e = pointer->getNumChildren(); Idx < e;) {
|
|
NodePointer child = pointer->getChild(Idx);
|
|
// Until we no longer have a type node, keep demangling.
|
|
if (child->getKind() != Node::Kind::Type)
|
|
break;
|
|
print(child);
|
|
++Idx;
|
|
|
|
// If we are not done, print the ", ".
|
|
if (Idx < e && pointer->getChild(Idx)->hasText())
|
|
Printer << ", ";
|
|
}
|
|
Printer << "]";
|
|
return Idx;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(
|
|
((V & unsigned(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) ||
|
|
(V & unsigned(FunctionSigSpecializationParamKind::SROA)) ||
|
|
(V & unsigned(FunctionSigSpecializationParamKind::Dead))) &&
|
|
"Invalid OptionSet");
|
|
print(pointer->getChild(Idx++));
|
|
return Idx;
|
|
}
|
|
|
|
static bool isClassType(NodePointer pointer) {
|
|
return pointer->getKind() == Node::Kind::Class;
|
|
}
|
|
|
|
static bool useColonForEntityType(NodePointer entity, NodePointer type) {
|
|
switch (entity->getKind()) {
|
|
case Node::Kind::Variable:
|
|
case Node::Kind::Initializer:
|
|
case Node::Kind::DefaultArgumentInitializer:
|
|
case Node::Kind::IVarInitializer:
|
|
case Node::Kind::Class:
|
|
case Node::Kind::Structure:
|
|
case Node::Kind::Enum:
|
|
case Node::Kind::Protocol:
|
|
case Node::Kind::TypeAlias:
|
|
case Node::Kind::OwningAddressor:
|
|
case Node::Kind::OwningMutableAddressor:
|
|
case Node::Kind::NativeOwningAddressor:
|
|
case Node::Kind::NativeOwningMutableAddressor:
|
|
case Node::Kind::NativePinningAddressor:
|
|
case Node::Kind::NativePinningMutableAddressor:
|
|
case Node::Kind::UnsafeAddressor:
|
|
case Node::Kind::UnsafeMutableAddressor:
|
|
case Node::Kind::GlobalGetter:
|
|
case Node::Kind::Getter:
|
|
case Node::Kind::Setter:
|
|
case Node::Kind::MaterializeForSet:
|
|
case Node::Kind::WillSet:
|
|
case Node::Kind::DidSet:
|
|
return true;
|
|
|
|
case Node::Kind::Subscript:
|
|
case Node::Kind::Function:
|
|
case Node::Kind::ExplicitClosure:
|
|
case Node::Kind::ImplicitClosure:
|
|
case Node::Kind::Allocator:
|
|
case Node::Kind::Constructor:
|
|
case Node::Kind::Destructor:
|
|
case Node::Kind::Deallocator:
|
|
case Node::Kind::IVarDestroyer: {
|
|
// We expect to see a function type here, but if we don't, use the colon.
|
|
type = type->getChild(0);
|
|
while (type->getKind() == Node::Kind::GenericType ||
|
|
type->getKind() == Node::Kind::DependentGenericType)
|
|
type = type->getChild(1)->getChild(0);
|
|
return (type->getKind() != Node::Kind::FunctionType &&
|
|
type->getKind() != Node::Kind::UncurriedFunctionType &&
|
|
type->getKind() != Node::Kind::CFunctionPointer &&
|
|
type->getKind() != Node::Kind::ThinFunctionType);
|
|
}
|
|
|
|
default:
|
|
unreachable("not an entity");
|
|
}
|
|
}
|
|
|
|
static bool isMethodContext(const NodePointer &context) {
|
|
switch (context->getKind()) {
|
|
case Node::Kind::Structure:
|
|
case Node::Kind::Enum:
|
|
case Node::Kind::Class:
|
|
case Node::Kind::Protocol:
|
|
case Node::Kind::Extension:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Perform any desired type simplifications for an entity in Simplified mode.
|
|
void NodePrinter::printSimplifiedEntityType(NodePointer context,
|
|
NodePointer entityType) {
|
|
// Only do anything special to methods.
|
|
if (!isMethodContext(context)) return print(entityType);
|
|
|
|
// Strip off a single level of uncurried function type.
|
|
NodePointer type = entityType;
|
|
assert(type->getKind() == Node::Kind::Type);
|
|
type = type->getChild(0);
|
|
|
|
NodePointer generics;
|
|
if (type->getKind() == Node::Kind::GenericType ||
|
|
type->getKind() == Node::Kind::DependentGenericType) {
|
|
generics = type->getChild(0);
|
|
type = type->getChild(1)->getChild(0);
|
|
}
|
|
|
|
print(entityType);
|
|
}
|
|
|
|
void NodePrinter::print(NodePointer pointer, bool asContext, bool suppressType) {
|
|
// Common code for handling entities.
|
|
auto printEntity = [&](bool hasName, bool hasType, StringRef extraName) {
|
|
if (Options.QualifyEntities)
|
|
printContext(pointer->getChild(0));
|
|
|
|
bool printType = (hasType && !suppressType);
|
|
bool useParens = (printType && asContext);
|
|
|
|
if (useParens) Printer << '(';
|
|
|
|
if (hasName) print(pointer->getChild(1));
|
|
Printer << extraName;
|
|
|
|
if (printType) {
|
|
NodePointer type = pointer->getChild(1 + unsigned(hasName));
|
|
if (useColonForEntityType(pointer, type)) {
|
|
if (Options.DisplayEntityTypes) {
|
|
Printer << " : ";
|
|
print(type);
|
|
}
|
|
} else if (!Options.DisplayEntityTypes) {
|
|
printSimplifiedEntityType(pointer->getChild(0), type);
|
|
} else {
|
|
Printer << " ";
|
|
print(type);
|
|
}
|
|
}
|
|
|
|
if (useParens) Printer << ')';
|
|
};
|
|
|
|
Node::Kind kind = pointer->getKind();
|
|
switch (kind) {
|
|
case Node::Kind::Static:
|
|
Printer << "static ";
|
|
print(pointer->getChild(0), asContext, suppressType);
|
|
return;
|
|
case Node::Kind::Directness:
|
|
Printer << toString(Directness(pointer->getIndex())) << " ";
|
|
return;
|
|
case Node::Kind::Extension:
|
|
assert((pointer->getNumChildren() == 2 || pointer->getNumChildren() == 3)
|
|
&& "Extension expects 2 or 3 children.");
|
|
if (Options.QualifyEntities && Options.DisplayExtensionContexts) {
|
|
Printer << "(extension in ";
|
|
// Print the module where extension is defined.
|
|
print(pointer->getChild(0), true);
|
|
Printer << "):";
|
|
}
|
|
print(pointer->getChild(1), asContext);
|
|
if (pointer->getNumChildren() == 3)
|
|
print(pointer->getChild(2), true);
|
|
return;
|
|
case Node::Kind::Variable:
|
|
case Node::Kind::Function:
|
|
case Node::Kind::Subscript:
|
|
printEntity(true, true, "");
|
|
return;
|
|
case Node::Kind::ExplicitClosure:
|
|
case Node::Kind::ImplicitClosure: {
|
|
auto index = pointer->getChild(1)->getIndex();
|
|
std::string name;
|
|
DemanglerPrinter printName(name);
|
|
printName << '(';
|
|
if (pointer->getKind() == Node::Kind::ImplicitClosure)
|
|
printName << "implicit ";
|
|
printName << "closure #" << (index + 1) << ")";
|
|
printEntity(false, false, name);
|
|
return;
|
|
}
|
|
case Node::Kind::Global:
|
|
printChildren(pointer);
|
|
return;
|
|
case Node::Kind::Suffix:
|
|
if (!Options.DisplayUnmangledSuffix) return;
|
|
Printer << " with unmangled suffix " << QuotedString(pointer->getText());
|
|
return;
|
|
case Node::Kind::Initializer:
|
|
printEntity(false, false, "(variable initialization expression)");
|
|
return;
|
|
case Node::Kind::DefaultArgumentInitializer: {
|
|
auto index = pointer->getChild(1);
|
|
std::string str;
|
|
DemanglerPrinter strPrinter(str);
|
|
strPrinter << "(default argument " << index->getIndex() << ")";
|
|
printEntity(false, false, str);
|
|
return;
|
|
}
|
|
case Node::Kind::DeclContext:
|
|
print(pointer->getChild(0), asContext);
|
|
return;
|
|
case Node::Kind::Type:
|
|
print(pointer->getChild(0), asContext);
|
|
return;
|
|
case Node::Kind::TypeMangling:
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::Class:
|
|
case Node::Kind::Structure:
|
|
case Node::Kind::Enum:
|
|
case Node::Kind::Protocol:
|
|
case Node::Kind::TypeAlias:
|
|
printEntity(true, false, "");
|
|
return;
|
|
case Node::Kind::LocalDeclName:
|
|
Printer << '(';
|
|
print(pointer->getChild(1));
|
|
Printer << " #" << (pointer->getChild(0)->getIndex() + 1) << ')';
|
|
return;
|
|
case Node::Kind::PrivateDeclName:
|
|
Printer << '(';
|
|
print(pointer->getChild(1));
|
|
Printer << " in " << pointer->getChild(0)->getText() << ')';
|
|
return;
|
|
case Node::Kind::Module:
|
|
if (Options.DisplayModuleNames)
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::Identifier:
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::Index:
|
|
Printer << pointer->getIndex();
|
|
return;
|
|
case Node::Kind::AutoClosureType:
|
|
Printer << "@autoclosure ";
|
|
printFunctionType(pointer);
|
|
return;
|
|
case Node::Kind::ThinFunctionType:
|
|
Printer << "@convention(thin) ";
|
|
printFunctionType(pointer);
|
|
return;
|
|
case Node::Kind::FunctionType:
|
|
case Node::Kind::UncurriedFunctionType:
|
|
printFunctionType(pointer);
|
|
return;
|
|
case Node::Kind::ArgumentTuple: {
|
|
bool need_parens = false;
|
|
if (pointer->getNumChildren() > 1)
|
|
need_parens = true;
|
|
else {
|
|
if (!pointer->hasChildren())
|
|
need_parens = true;
|
|
else {
|
|
Node::Kind child0_kind = pointer->getChild(0)->getChild(0)->getKind();
|
|
if (child0_kind != Node::Kind::VariadicTuple &&
|
|
child0_kind != Node::Kind::NonVariadicTuple)
|
|
need_parens = true;
|
|
}
|
|
}
|
|
if (need_parens)
|
|
Printer << "(";
|
|
printChildren(pointer);
|
|
if (need_parens)
|
|
Printer << ")";
|
|
return;
|
|
}
|
|
case Node::Kind::NonVariadicTuple:
|
|
case Node::Kind::VariadicTuple: {
|
|
Printer << "(";
|
|
printChildren(pointer, ", ");
|
|
if (pointer->getKind() == Node::Kind::VariadicTuple)
|
|
Printer << "...";
|
|
Printer << ")";
|
|
return;
|
|
}
|
|
case Node::Kind::TupleElement:
|
|
if (pointer->getNumChildren() == 1) {
|
|
NodePointer type = pointer->getChild(0);
|
|
print(type);
|
|
} else if (pointer->getNumChildren() == 2) {
|
|
NodePointer id = pointer->getChild(0);
|
|
NodePointer type = pointer->getChild(1);
|
|
print(id);
|
|
print(type);
|
|
}
|
|
return;
|
|
case Node::Kind::TupleElementName:
|
|
Printer << pointer->getText() << " : ";
|
|
return;
|
|
case Node::Kind::ReturnType:
|
|
if (pointer->getNumChildren() == 0)
|
|
Printer << " -> " << pointer->getText();
|
|
else {
|
|
Printer << " -> ";
|
|
printChildren(pointer);
|
|
}
|
|
return;
|
|
case Node::Kind::Weak:
|
|
Printer << "weak ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::Unowned:
|
|
Printer << "unowned ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::Unmanaged:
|
|
Printer << "unowned(unsafe) ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::InOut:
|
|
Printer << "inout ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::NonObjCAttribute:
|
|
Printer << "@nonobjc ";
|
|
return;
|
|
case Node::Kind::ObjCAttribute:
|
|
Printer << "@objc ";
|
|
return;
|
|
case Node::Kind::DirectMethodReferenceAttribute:
|
|
Printer << "super ";
|
|
return;
|
|
case Node::Kind::DynamicAttribute:
|
|
Printer << "dynamic ";
|
|
return;
|
|
case Node::Kind::VTableAttribute:
|
|
Printer << "override ";
|
|
return;
|
|
case Node::Kind::FunctionSignatureSpecialization:
|
|
case Node::Kind::GenericSpecialization:
|
|
case Node::Kind::GenericSpecializationNotReAbstracted: {
|
|
if (!Options.DisplayGenericSpecializations) {
|
|
Printer << "specialized ";
|
|
return;
|
|
}
|
|
if (pointer->getKind() == Node::Kind::FunctionSignatureSpecialization) {
|
|
Printer << "function signature specialization <";
|
|
} else if (pointer->getKind() == Node::Kind::GenericSpecialization) {
|
|
Printer << "generic specialization <";
|
|
} else {
|
|
Printer << "generic not re-abstracted specialization <";
|
|
}
|
|
bool hasPrevious = false;
|
|
// We skip the 0 index since the SpecializationPassID does not contain any
|
|
// information that is useful to our users.
|
|
for (unsigned i = 1, e = pointer->getNumChildren(); i < e; ++i) {
|
|
// Ignore empty specializations.
|
|
if (!pointer->getChild(i)->hasChildren())
|
|
continue;
|
|
if (hasPrevious)
|
|
Printer << ", ";
|
|
print(pointer->getChild(i));
|
|
hasPrevious = true;
|
|
}
|
|
Printer << "> of ";
|
|
return;
|
|
}
|
|
case Node::Kind::GenericSpecializationParam:
|
|
print(pointer->getChild(0));
|
|
for (unsigned i = 1, e = pointer->getNumChildren(); i < e; ++i) {
|
|
if (i == 1)
|
|
Printer << " with ";
|
|
else
|
|
Printer << " and ";
|
|
print(pointer->getChild(i));
|
|
}
|
|
return;
|
|
case Node::Kind::FunctionSignatureSpecializationParam: {
|
|
uint64_t argNum = pointer->getIndex();
|
|
|
|
Printer << "Arg[" << argNum << "] = ";
|
|
|
|
unsigned Idx = printFunctionSigSpecializationParam(pointer, 0);
|
|
|
|
for (unsigned e = pointer->getNumChildren(); Idx < e;) {
|
|
Printer << " and ";
|
|
Idx = printFunctionSigSpecializationParam(pointer, Idx);
|
|
}
|
|
|
|
return;
|
|
}
|
|
case Node::Kind::FunctionSignatureSpecializationParamPayload: {
|
|
std::string demangledName = demangleSymbolAsString(pointer->getText());
|
|
if (demangledName.empty()) {
|
|
Printer << pointer->getText();
|
|
} else {
|
|
Printer << demangledName;
|
|
}
|
|
return;
|
|
}
|
|
case Node::Kind::FunctionSignatureSpecializationParamKind: {
|
|
uint64_t raw = pointer->getIndex();
|
|
|
|
bool printedOptionSet = false;
|
|
if (raw & uint64_t(FunctionSigSpecializationParamKind::Dead)) {
|
|
printedOptionSet = true;
|
|
Printer << "Dead";
|
|
}
|
|
|
|
if (raw & uint64_t(FunctionSigSpecializationParamKind::OwnedToGuaranteed)) {
|
|
if (printedOptionSet)
|
|
Printer << " and ";
|
|
printedOptionSet = true;
|
|
Printer << "Owned To Guaranteed";
|
|
}
|
|
|
|
if (raw & uint64_t(FunctionSigSpecializationParamKind::SROA)) {
|
|
if (printedOptionSet)
|
|
Printer << " and ";
|
|
Printer << "Exploded";
|
|
return;
|
|
}
|
|
|
|
if (printedOptionSet)
|
|
return;
|
|
|
|
switch (FunctionSigSpecializationParamKind(raw)) {
|
|
case FunctionSigSpecializationParamKind::BoxToValue:
|
|
Printer << "Value Promoted from Box";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::BoxToStack:
|
|
Printer << "Stack Promoted from Box";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ConstantPropFunction:
|
|
Printer << "Constant Propagated Function";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ConstantPropGlobal:
|
|
Printer << "Constant Propagated Global";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ConstantPropInteger:
|
|
Printer << "Constant Propagated Integer";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ConstantPropFloat:
|
|
Printer << "Constant Propagated Float";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ConstantPropString:
|
|
Printer << "Constant Propagated String";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::ClosureProp:
|
|
Printer << "Closure Propagated";
|
|
break;
|
|
case FunctionSigSpecializationParamKind::Dead:
|
|
case FunctionSigSpecializationParamKind::OwnedToGuaranteed:
|
|
case FunctionSigSpecializationParamKind::SROA:
|
|
unreachable("option sets should have been handled earlier");
|
|
}
|
|
return;
|
|
}
|
|
case Node::Kind::SpecializationPassID:
|
|
Printer << pointer->getIndex();
|
|
return;
|
|
case Node::Kind::BuiltinTypeName:
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::Number:
|
|
Printer << pointer->getIndex();
|
|
return;
|
|
case Node::Kind::InfixOperator:
|
|
Printer << pointer->getText() << " infix";
|
|
return;
|
|
case Node::Kind::PrefixOperator:
|
|
Printer << pointer->getText() << " prefix";
|
|
return;
|
|
case Node::Kind::PostfixOperator:
|
|
Printer << pointer->getText() << " postfix";
|
|
return;
|
|
case Node::Kind::LazyProtocolWitnessTableAccessor:
|
|
Printer << "lazy protocol witness table accessor for type ";
|
|
print(pointer->getChild(0));
|
|
Printer << " and conformance ";
|
|
print(pointer->getChild(1));
|
|
return;
|
|
case Node::Kind::LazyProtocolWitnessTableCacheVariable:
|
|
Printer << "lazy protocol witness table cache variable for type ";
|
|
print(pointer->getChild(0));
|
|
Printer << " and conformance ";
|
|
print(pointer->getChild(1));
|
|
return;
|
|
case Node::Kind::ProtocolWitnessTableAccessor:
|
|
Printer << "protocol witness table accessor for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::ProtocolWitnessTable:
|
|
Printer << "protocol witness table for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::GenericProtocolWitnessTable:
|
|
Printer << "generic protocol witness table for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::GenericProtocolWitnessTableInstantiationFunction:
|
|
Printer << "instantiation function for generic protocol witness table for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::ProtocolWitness: {
|
|
Printer << "protocol witness for ";
|
|
print(pointer->getChild(1));
|
|
Printer << " in conformance ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
}
|
|
case Node::Kind::PartialApplyForwarder:
|
|
if (Options.ShortenPartialApply)
|
|
Printer << "partial apply";
|
|
else
|
|
Printer << "partial apply forwarder";
|
|
|
|
if (pointer->hasChildren()) {
|
|
Printer << " for ";
|
|
print(pointer->getFirstChild());
|
|
}
|
|
return;
|
|
case Node::Kind::PartialApplyObjCForwarder:
|
|
if (Options.ShortenPartialApply)
|
|
Printer << "partial apply";
|
|
else
|
|
Printer << "partial apply ObjC forwarder";
|
|
|
|
if (pointer->hasChildren()) {
|
|
Printer << " for ";
|
|
print(pointer->getFirstChild());
|
|
}
|
|
return;
|
|
case Node::Kind::FieldOffset: {
|
|
print(pointer->getChild(0)); // directness
|
|
Printer << "field offset for ";
|
|
auto entity = pointer->getChild(1);
|
|
print(entity, /*asContext*/ false,
|
|
/*suppressType*/ !Options.DisplayTypeOfIVarFieldOffset);
|
|
return;
|
|
}
|
|
case Node::Kind::ReabstractionThunk:
|
|
case Node::Kind::ReabstractionThunkHelper: {
|
|
if (Options.ShortenThunk) {
|
|
Printer << "thunk";
|
|
return;
|
|
}
|
|
Printer << "reabstraction thunk ";
|
|
if (pointer->getKind() == Node::Kind::ReabstractionThunkHelper)
|
|
Printer << "helper ";
|
|
auto generics = getFirstChildOfKind(pointer, Node::Kind::DependentGenericSignature);
|
|
assert(pointer->getNumChildren() == 2 + unsigned(generics != nullptr));
|
|
if (generics) {
|
|
print(generics);
|
|
Printer << " ";
|
|
}
|
|
Printer << "from ";
|
|
print(pointer->getChild(pointer->getNumChildren() - 2));
|
|
Printer << " to ";
|
|
print(pointer->getChild(pointer->getNumChildren() - 1));
|
|
return;
|
|
}
|
|
case Node::Kind::GenericTypeMetadataPattern:
|
|
Printer << "generic type metadata pattern for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::Metaclass:
|
|
Printer << "metaclass for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::ProtocolDescriptor:
|
|
Printer << "protocol descriptor for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::FullTypeMetadata:
|
|
Printer << "full type metadata for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::TypeMetadata:
|
|
Printer << "type metadata for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::TypeMetadataAccessFunction:
|
|
Printer << "type metadata accessor for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::TypeMetadataLazyCache:
|
|
Printer << "lazy cache variable for type metadata for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::AssociatedTypeMetadataAccessor:
|
|
Printer << "associated type metadata accessor for ";
|
|
print(pointer->getChild(1));
|
|
Printer << " in ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::AssociatedTypeWitnessTableAccessor:
|
|
Printer << "associated type witness table accessor for ";
|
|
print(pointer->getChild(1));
|
|
Printer << " : ";
|
|
print(pointer->getChild(2));
|
|
Printer << " in ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::NominalTypeDescriptor:
|
|
Printer << "nominal type descriptor for ";
|
|
print(pointer->getChild(0));
|
|
return;
|
|
case Node::Kind::ValueWitness:
|
|
Printer << toString(ValueWitnessKind(pointer->getIndex()));
|
|
if (Options.ShortenValueWitness) Printer << " for ";
|
|
else Printer << " value witness for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::ValueWitnessTable:
|
|
Printer << "value witness table for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::WitnessTableOffset:
|
|
Printer << "witness table offset for ";
|
|
print(pointer->getFirstChild());
|
|
return;
|
|
case Node::Kind::BoundGenericClass:
|
|
case Node::Kind::BoundGenericStructure:
|
|
case Node::Kind::BoundGenericEnum:
|
|
printBoundGeneric(pointer);
|
|
return;
|
|
case Node::Kind::DynamicSelf:
|
|
Printer << "Self";
|
|
return;
|
|
case Node::Kind::CFunctionPointer: {
|
|
Printer << "@convention(c) ";
|
|
printFunctionType(pointer);
|
|
return;
|
|
}
|
|
case Node::Kind::ObjCBlock: {
|
|
Printer << "@convention(block) ";
|
|
printFunctionType(pointer);
|
|
return;
|
|
}
|
|
case Node::Kind::SILBoxType: {
|
|
Printer << "@box ";
|
|
NodePointer type = pointer->getChild(0);
|
|
print(type);
|
|
return;
|
|
}
|
|
case Node::Kind::Metatype: {
|
|
unsigned Idx = 0;
|
|
if (pointer->getNumChildren() == 2) {
|
|
NodePointer repr = pointer->getChild(Idx);
|
|
print(repr);
|
|
Printer << " ";
|
|
Idx++;
|
|
}
|
|
NodePointer type = pointer->getChild(Idx);
|
|
print(type);
|
|
if (isExistentialType(type)) {
|
|
Printer << ".Protocol";
|
|
} else {
|
|
Printer << ".Type";
|
|
}
|
|
return;
|
|
}
|
|
case Node::Kind::ExistentialMetatype: {
|
|
unsigned Idx = 0;
|
|
if (pointer->getNumChildren() == 2) {
|
|
NodePointer repr = pointer->getChild(Idx);
|
|
print(repr);
|
|
Printer << " ";
|
|
Idx++;
|
|
}
|
|
|
|
NodePointer type = pointer->getChild(Idx);
|
|
print(type);
|
|
Printer << ".Type";
|
|
return;
|
|
}
|
|
case Node::Kind::MetatypeRepresentation: {
|
|
Printer << pointer->getText();
|
|
return;
|
|
}
|
|
case Node::Kind::ArchetypeRef:
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::AssociatedTypeRef:
|
|
print(pointer->getChild(0));
|
|
Printer << '.' << pointer->getChild(1)->getText();
|
|
return;
|
|
case Node::Kind::SelfTypeRef:
|
|
print(pointer->getChild(0));
|
|
Printer << ".Self";
|
|
return;
|
|
case Node::Kind::ProtocolList: {
|
|
NodePointer type_list = pointer->getChild(0);
|
|
if (!type_list)
|
|
return;
|
|
bool needs_proto_marker = (type_list->getNumChildren() != 1);
|
|
if (needs_proto_marker)
|
|
Printer << "protocol<";
|
|
printChildren(type_list, ", ");
|
|
if (needs_proto_marker)
|
|
Printer << ">";
|
|
return;
|
|
}
|
|
case Node::Kind::Generics: {
|
|
if (pointer->getNumChildren() == 0)
|
|
return;
|
|
Printer << "<";
|
|
print(pointer->getChild(0));
|
|
for (unsigned i = 1, e = pointer->getNumChildren(); i != e; ++i) {
|
|
auto child = pointer->getChild(i);
|
|
if (child->getKind() != Node::Kind::Archetype) break;
|
|
Printer << ", ";
|
|
print(child);
|
|
}
|
|
Printer << ">";
|
|
return;
|
|
}
|
|
case Node::Kind::Archetype: {
|
|
Printer << pointer->getText();
|
|
if (pointer->hasChildren()) {
|
|
Printer << " : ";
|
|
print(pointer->getChild(0));
|
|
}
|
|
return;
|
|
}
|
|
case Node::Kind::AssociatedType:
|
|
// Don't print for now.
|
|
return;
|
|
case Node::Kind::QualifiedArchetype: {
|
|
if (Options.ShortenArchetype) {
|
|
Printer << "(archetype)";
|
|
return;
|
|
}
|
|
if (pointer->getNumChildren() < 2)
|
|
return;
|
|
NodePointer number = pointer->getChild(0);
|
|
NodePointer decl_ctx = pointer->getChild(1);
|
|
Printer << "(archetype " << number->getIndex() << " of ";
|
|
print(decl_ctx);
|
|
Printer << ")";
|
|
return;
|
|
}
|
|
case Node::Kind::GenericType: {
|
|
NodePointer atype_list = pointer->getChild(0);
|
|
NodePointer fct_type = pointer->getChild(1)->getChild(0);
|
|
print(atype_list);
|
|
print(fct_type);
|
|
return;
|
|
}
|
|
case Node::Kind::OwningAddressor:
|
|
printEntity(true, true, ".owningAddressor");
|
|
return;
|
|
case Node::Kind::OwningMutableAddressor:
|
|
printEntity(true, true, ".owningMutableAddressor");
|
|
return;
|
|
case Node::Kind::NativeOwningAddressor:
|
|
printEntity(true, true, ".nativeOwningAddressor");
|
|
return;
|
|
case Node::Kind::NativeOwningMutableAddressor:
|
|
printEntity(true, true, ".nativeOwningMutableAddressor");
|
|
return;
|
|
case Node::Kind::NativePinningAddressor:
|
|
printEntity(true, true, ".nativePinningAddressor");
|
|
return;
|
|
case Node::Kind::NativePinningMutableAddressor:
|
|
printEntity(true, true, ".nativePinningMutableAddressor");
|
|
return;
|
|
case Node::Kind::UnsafeAddressor:
|
|
printEntity(true, true, ".unsafeAddressor");
|
|
return;
|
|
case Node::Kind::UnsafeMutableAddressor:
|
|
printEntity(true, true, ".unsafeMutableAddressor");
|
|
return;
|
|
case Node::Kind::GlobalGetter:
|
|
printEntity(true, true, ".getter");
|
|
return;
|
|
case Node::Kind::Getter:
|
|
printEntity(true, true, ".getter");
|
|
return;
|
|
case Node::Kind::Setter:
|
|
printEntity(true, true, ".setter");
|
|
return;
|
|
case Node::Kind::MaterializeForSet:
|
|
printEntity(true, true, ".materializeForSet");
|
|
return;
|
|
case Node::Kind::WillSet:
|
|
printEntity(true, true, ".willset");
|
|
return;
|
|
case Node::Kind::DidSet:
|
|
printEntity(true, true, ".didset");
|
|
return;
|
|
case Node::Kind::Allocator:
|
|
printEntity(false, true,
|
|
isClassType(pointer->getChild(0))
|
|
? "__allocating_init" : "init");
|
|
return;
|
|
case Node::Kind::Constructor:
|
|
printEntity(false, true, "init");
|
|
return;
|
|
case Node::Kind::Destructor:
|
|
printEntity(false, false, "deinit");
|
|
return;
|
|
case Node::Kind::Deallocator:
|
|
printEntity(false, false,
|
|
isClassType(pointer->getChild(0))
|
|
? "__deallocating_deinit" : "deinit");
|
|
return;
|
|
case Node::Kind::IVarInitializer:
|
|
printEntity(false, false, "__ivar_initializer");
|
|
return;
|
|
case Node::Kind::IVarDestroyer:
|
|
printEntity(false, false, "__ivar_destroyer");
|
|
return;
|
|
case Node::Kind::ProtocolConformance: {
|
|
NodePointer child0 = pointer->getChild(0);
|
|
NodePointer child1 = pointer->getChild(1);
|
|
NodePointer child2 = pointer->getChild(2);
|
|
print(child0);
|
|
if (Options.DisplayProtocolConformances) {
|
|
Printer << " : ";
|
|
print(child1);
|
|
Printer << " in ";
|
|
print(child2);
|
|
}
|
|
return;
|
|
}
|
|
case Node::Kind::TypeList:
|
|
printChildren(pointer);
|
|
return;
|
|
case Node::Kind::ImplConvention:
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::ImplFunctionAttribute:
|
|
Printer << pointer->getText();
|
|
return;
|
|
case Node::Kind::ImplErrorResult:
|
|
Printer << "@error ";
|
|
SWIFT_FALLTHROUGH;
|
|
case Node::Kind::ImplParameter:
|
|
case Node::Kind::ImplResult:
|
|
printChildren(pointer, " ");
|
|
return;
|
|
case Node::Kind::ImplFunctionType:
|
|
printImplFunctionType(pointer);
|
|
return;
|
|
case Node::Kind::ErrorType:
|
|
Printer << "<ERROR TYPE>";
|
|
return;
|
|
|
|
case Node::Kind::DependentGenericSignature: {
|
|
Printer << '<';
|
|
|
|
unsigned depth = 0;
|
|
unsigned numChildren = pointer->getNumChildren();
|
|
for (;
|
|
depth < numChildren
|
|
&& pointer->getChild(depth)->getKind()
|
|
== Node::Kind::DependentGenericParamCount;
|
|
++depth) {
|
|
if (depth != 0)
|
|
Printer << "><";
|
|
|
|
unsigned count = pointer->getChild(depth)->getIndex();
|
|
for (unsigned index = 0; index < count; ++index) {
|
|
if (index != 0)
|
|
Printer << ", ";
|
|
// FIXME: Depth won't match when a generic signature applies to a
|
|
// method in generic type context.
|
|
Printer << archetypeName(index, depth);
|
|
}
|
|
}
|
|
|
|
if (depth != numChildren) {
|
|
if (!Options.DisplayWhereClauses) {
|
|
Printer << " where ...";
|
|
} else {
|
|
Printer << " where ";
|
|
for (unsigned i = depth; i < numChildren; ++i) {
|
|
if (i > depth)
|
|
Printer << ", ";
|
|
print(pointer->getChild(i));
|
|
}
|
|
}
|
|
}
|
|
Printer << '>';
|
|
return;
|
|
}
|
|
case Node::Kind::DependentGenericParamCount:
|
|
unreachable("should be printed as a child of a "
|
|
"DependentGenericSignature");
|
|
case Node::Kind::DependentGenericConformanceRequirement: {
|
|
NodePointer type = pointer->getChild(0);
|
|
NodePointer reqt = pointer->getChild(1);
|
|
print(type);
|
|
Printer << ": ";
|
|
print(reqt);
|
|
return;
|
|
}
|
|
case Node::Kind::DependentGenericSameTypeRequirement: {
|
|
NodePointer fst = pointer->getChild(0);
|
|
NodePointer snd = pointer->getChild(1);
|
|
|
|
print(fst);
|
|
Printer << " == ";
|
|
print(snd);
|
|
return;
|
|
}
|
|
case Node::Kind::DependentGenericParamType: {
|
|
Printer << pointer->getText();
|
|
return;
|
|
}
|
|
case Node::Kind::DependentGenericType: {
|
|
NodePointer sig = pointer->getChild(0);
|
|
NodePointer depTy = pointer->getChild(1);
|
|
print(sig);
|
|
Printer << ' ';
|
|
print(depTy);
|
|
return;
|
|
}
|
|
case Node::Kind::DependentMemberType: {
|
|
NodePointer base = pointer->getChild(0);
|
|
print(base);
|
|
Printer << '.';
|
|
NodePointer assocTy = pointer->getChild(1);
|
|
print(assocTy);
|
|
return;
|
|
}
|
|
case Node::Kind::DependentAssociatedTypeRef: {
|
|
Printer << pointer->getText();
|
|
return;
|
|
}
|
|
case Node::Kind::ThrowsAnnotation: {
|
|
Printer<< " throws ";
|
|
return;
|
|
}
|
|
}
|
|
unreachable("bad node kind!");
|
|
}
|
|
|
|
std::string Demangle::nodeToString(NodePointer root,
|
|
const DemangleOptions &options) {
|
|
if (!root)
|
|
return "";
|
|
|
|
return NodePrinter(options).printRoot(root);
|
|
}
|
|
|
|
std::string Demangle::demangleSymbolAsString(const char *MangledName,
|
|
size_t MangledNameLength,
|
|
const DemangleOptions &Options) {
|
|
auto mangled = StringRef(MangledName, MangledNameLength);
|
|
auto root = demangleSymbolAsNode(MangledName, MangledNameLength, Options);
|
|
if (!root) return mangled.str();
|
|
|
|
std::string demangling = nodeToString(std::move(root), Options);
|
|
if (demangling.empty())
|
|
return mangled.str();
|
|
return demangling;
|
|
}
|
|
|
|
std::string Demangle::demangleTypeAsString(const char *MangledName,
|
|
size_t MangledNameLength,
|
|
const DemangleOptions &Options) {
|
|
auto mangled = StringRef(MangledName, MangledNameLength);
|
|
auto root = demangleTypeAsNode(MangledName, MangledNameLength, Options);
|
|
if (!root) return mangled.str();
|
|
|
|
std::string demangling = nodeToString(std::move(root), Options);
|
|
if (demangling.empty())
|
|
return mangled.str();
|
|
return demangling;
|
|
}
|
|
|