//===--- Demangler.h - String to Node-Tree Demangling -----------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file is the compiler-private API of the demangler. // It should only be used within the swift compiler or runtime library, but not // by external tools which use the demangler library (like lldb). // //===----------------------------------------------------------------------===// #ifndef SWIFT_DEMANGLING_DEMANGLER_H #define SWIFT_DEMANGLING_DEMANGLER_H #include "swift/Demangling/Demangle.h" #include "swift/Demangling/ManglingFlavor.h" #include "swift/Demangling/NamespaceMacros.h" //#define NODE_FACTORY_DEBUGGING using namespace swift::Demangle; using llvm::StringRef; namespace swift { namespace Demangle { SWIFT_BEGIN_INLINE_NAMESPACE class CharVector; /// The allocator for demangling nodes and other demangling-internal stuff. /// /// It implements a simple bump-pointer allocator. class NodeFactory { /// Position in the current slab. char *CurPtr = nullptr; /// The end of the current slab. char *End = nullptr; struct AllocatedSlab { // The previously allocated slab. AllocatedSlab *Previous; // Tail allocated memory starts here. }; /// The head of the single-linked slab list. AllocatedSlab *CurrentSlab = nullptr; /// The size of the previously allocated slab. This may NOT be the size of /// CurrentSlab, in the case where a checkpoint has been popped. /// /// The slab size can only grow, even clear() does not reset the slab size. /// This initial size is good enough to fit most de-manglings. size_t SlabSize = 100 * sizeof(Node); static char *align(char *Ptr, size_t Alignment) { assert(Alignment > 0); return (char*)(((uintptr_t)Ptr + Alignment - 1) & ~((uintptr_t)Alignment - 1)); } static void freeSlabs(AllocatedSlab *slab); /// If not null, the NodeFactory from which this factory borrowed free memory. NodeFactory *BorrowedFrom = nullptr; /// True if some other NodeFactory borrowed free memory from this factory. bool isBorrowed = false; #ifdef NODE_FACTORY_DEBUGGING size_t allocatedMemory = 0; static int nestingLevel; std::string indent() { return std::string(nestingLevel * 2, ' '); } #endif public: #ifndef NDEBUG /// Enabled only by the unit tests to test the failure paths. bool disableAssertionsForUnitTest = false; #endif NodeFactory() { #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%s## New NodeFactory\n", indent().c_str()); nestingLevel++; #endif } /// Provide pre-allocated memory, e.g. memory on the stack. /// /// Only if this memory overflows, the factory begins to malloc. void providePreallocatedMemory(char *Memory, size_t Size) { #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%s++ provide preallocated memory, size = %zu\n", indent().c_str(), Size); #endif assert(!CurPtr && !End && !CurrentSlab); CurPtr = Memory; End = CurPtr + Size; } /// Borrow free memory from another factory \p BorrowFrom. /// /// While this factory is alive, no allocations can be done in the /// \p BorrowFrom factory. void providePreallocatedMemory(NodeFactory &BorrowFrom) { assert(!CurPtr && !End && !CurrentSlab); assert(!BorrowFrom.isBorrowed && !BorrowedFrom); BorrowFrom.isBorrowed = true; BorrowedFrom = &BorrowFrom; CurPtr = BorrowFrom.CurPtr; End = BorrowFrom.End; #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%s++ borrow memory, size = %zu\n", indent().c_str(), (End - CurPtr)); #endif } virtual ~NodeFactory() { freeSlabs(CurrentSlab); #ifdef NODE_FACTORY_DEBUGGING nestingLevel--; fprintf(stderr, "%s## Delete NodeFactory: allocated memory = %zu\n", indent().c_str(), allocatedMemory) #endif if (BorrowedFrom) { BorrowedFrom->isBorrowed = false; } } virtual void clear(); /// Allocates an object of type T or an array of objects of type T. template T *Allocate(size_t NumObjects = 1) { assert(!isBorrowed); size_t ObjectSize = NumObjects * sizeof(T); CurPtr = align(CurPtr, alignof(T)); #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%salloc %zu, CurPtr = %p\n", indent().c_str(), ObjectSize, (void *)CurPtr) allocatedMemory += ObjectSize; #endif // Do we have enough space in the current slab? if (!CurPtr || CurPtr + ObjectSize > End) { // No. We have to malloc a new slab. // We double the slab size for each allocated slab. SlabSize = std::max(SlabSize * 2, ObjectSize + alignof(T)); size_t AllocSize = sizeof(AllocatedSlab) + SlabSize; AllocatedSlab *newSlab = (AllocatedSlab *)malloc(AllocSize); // Insert the new slab in the single-linked list of slabs. newSlab->Previous = CurrentSlab; CurrentSlab = newSlab; // Initialize the pointers to the new slab. CurPtr = align((char *)(newSlab + 1), alignof(T)); End = (char *)newSlab + AllocSize; assert(CurPtr + ObjectSize <= End); #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%s** new slab %p, allocsize = %zu, CurPtr = %p, End = %p\n", indent().c_str(), newSlab, AllocSize, (void *)CurPtr, (void *)End); #endif } T *AllocatedObj = (T *)CurPtr; CurPtr += ObjectSize; return AllocatedObj; } /// Tries to enlarge the \p Capacity of an array of \p Objects. /// /// If \p Objects is allocated at the end of the current slab and the slab /// has enough free space, the \p Capacity is simply enlarged and no new /// allocation needs to be done. /// Otherwise a new array of objects is allocated and \p Objects is set to the /// new memory address. /// The \p Capacity is enlarged at least by \p MinGrowth, but can also be /// enlarged by a bigger value. template void Reallocate(T *&Objects, uint32_t &Capacity, size_t MinGrowth) { assert(!isBorrowed); size_t OldAllocSize = Capacity * sizeof(T); size_t AdditionalAlloc = MinGrowth * sizeof(T); #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%srealloc: capacity = %d (size = %zu), growth = %zu (size = %zu)\n", indent().c_str(), Capacity, OldAllocSize, MinGrowth, AdditionalAlloc); #endif if ((char *)Objects + OldAllocSize == CurPtr && CurPtr + AdditionalAlloc <= End) { // The existing array is at the end of the current slab and there is // enough space. So we are fine. CurPtr += AdditionalAlloc; Capacity += MinGrowth; #ifdef NODE_FACTORY_DEBUGGING fprintf(stderr, "%s** can grow: %p\n", indent().c_str(), (void *)CurPtr); allocatedMemory += AdditionalAlloc; #endif return; } // We need a new allocation. size_t Growth = (MinGrowth >= 4 ? MinGrowth : 4); if (Growth < Capacity * 2) Growth = Capacity * 2; T *NewObjects = Allocate(Capacity + Growth); if (OldAllocSize) memcpy(NewObjects, Objects, OldAllocSize); Objects = NewObjects; Capacity += Growth; } /// Copy a std::string to memory managed by the NodeFactory, returning a /// StringRef pointing to the copied string data. StringRef copyString(const std::string &str) { size_t stringSize = str.size() + 1; // + 1 for terminating NUL. char *copiedString = Allocate(stringSize); memcpy(copiedString, str.data(), stringSize); return {copiedString, str.size()}; } /// A checkpoint which captures the allocator's state at any given time. A /// checkpoint can be popped to free all allocations made since it was made. struct Checkpoint { AllocatedSlab *Slab; char *CurPtr; char *End; }; /// Create a new checkpoint with the current state of the allocator. Checkpoint pushCheckpoint() const; /// Clear all allocations made since the given checkpoint. It is /// undefined behavior to pop checkpoints in an order other than the /// order in which they were pushed, or to pop a checkpoint when /// clear() was called after creating it. The implementation attempts /// to raise a fatal error in that case, but does not guarantee it. It /// is allowed to pop outer checkpoints without popping inner ones, or /// to call clear() without popping existing checkpoints. void popCheckpoint(Checkpoint checkpoint); /// Creates a node of kind \p K. NodePointer createNode(Node::Kind K); /// Creates a node of kind \p K with an \p Index payload. NodePointer createNode(Node::Kind K, Node::IndexType Index); /// Creates a node of kind \p K with a \p RemoteAddress payload. /// /// These nodes are created and consumed by the reflection library. NodePointer createNode(Node::Kind K, uint64_t RemoteAddress, uint8_t AddressSpace); /// Creates a node of kind \p K with a \p Text payload. /// /// The \p Text string must be already allocated with the Factory and therefore /// it is _not_ copied. NodePointer createNodeWithAllocatedText(Node::Kind K, llvm::StringRef Text); /// Creates a node of kind \p K with a \p Text payload. /// /// The \p Text string is copied. NodePointer createNode(Node::Kind K, llvm::StringRef Text) { return createNodeWithAllocatedText(K, Text.copy(*this)); } /// Creates a node of kind \p K with a \p Text payload. /// /// The \p Text string is already allocated with the Factory and therefore /// it is _not_ copied. NodePointer createNode(Node::Kind K, const CharVector &Text); /// Creates a node of kind \p K with a \p Text payload, which must be a C /// string literal. /// /// The \p Text string is _not_ copied. NodePointer createNode(Node::Kind K, const char *Text); }; /// A vector with a storage managed by a NodeFactory. /// /// This Vector class only provides the minimal functionality needed by the /// Demangler. template class Vector { protected: T *Elems = nullptr; uint32_t NumElems = 0; uint32_t Capacity = 0; public: using iterator = T *; Vector() { } /// Construct a vector with an initial capacity. explicit Vector(NodeFactory &Factory, size_t InitialCapacity) { init(Factory, InitialCapacity); } /// Clears the content and re-allocates the buffer with an initial capacity. void init(NodeFactory &Factory, size_t InitialCapacity) { Elems = Factory.Allocate(InitialCapacity); NumElems = 0; Capacity = InitialCapacity; } void free() { Capacity = 0; Elems = 0; } void clear() { NumElems = 0; } iterator begin() { return Elems; } iterator end() { return Elems + NumElems; } T &operator[](size_t Idx) { assert(Idx < NumElems); return Elems[Idx]; } const T &operator[](size_t Idx) const { assert(Idx < NumElems); return Elems[Idx]; } size_t size() const { return NumElems; } bool empty() const { return NumElems == 0; } T &back() { return (*this)[NumElems - 1]; } void resetSize(size_t toPos) { assert(toPos <= NumElems); NumElems = toPos; } void push_back(const T &NewElem, NodeFactory &Factory) { if (NumElems >= Capacity) Factory.Reallocate(Elems, Capacity, /*Growth*/ 1); assert(NumElems < Capacity); Elems[NumElems++] = NewElem; } T pop_back_val() { if (empty()) return T(); T Val = (*this)[NumElems - 1]; NumElems--; return Val; } }; /// A vector of chars (a string) with a storage managed by a NodeFactory. /// /// This CharVector class only provides the minimal functionality needed by the /// Demangler. class CharVector : public Vector { public: // Append another string. void append(StringRef Rhs, NodeFactory &Factory); // Append an integer as readable number. void append(int Number, NodeFactory &Factory); // Append an unsigned 64 bit integer as readable number. void append(unsigned long long Number, NodeFactory &Factory); StringRef str() const { return StringRef(Elems, NumElems); } }; /// Kinds of symbolic reference supported. enum class SymbolicReferenceKind : uint8_t { /// A symbolic reference to a context descriptor, representing the /// (unapplied generic) context. Context, /// A symbolic reference to an accessor function, which can be executed in /// the process to get a pointer to the referenced entity. AccessorFunctionReference, /// A symbolic reference to a unique extended existential type shape. UniqueExtendedExistentialTypeShape, /// A symbolic reference to a non-unique extended existential type shape. NonUniqueExtendedExistentialTypeShape, /// A symbolic reference to a objective C protocol ref. ObjectiveCProtocol, }; using SymbolicReferenceResolver_t = NodePointer (SymbolicReferenceKind, Directness, int32_t, const void *); /// The demangler. /// /// It de-mangles a string and it also owns the returned node-tree. This means /// The nodes of the tree only live as long as the Demangler itself. class Demangler : public NodeFactory { protected: StringRef Text; size_t Pos = 0; /// Mangling style where function type would have /// labels attached to it, instead of having them /// as part of the name. bool IsOldFunctionTypeMangling = false; Mangle::ManglingFlavor Flavor = Mangle::ManglingFlavor::Default; Vector NodeStack; Vector Substitutions; static const int MaxNumWords = 26; StringRef Words[MaxNumWords]; int NumWords = 0; std::function SymbolicReferenceResolver; bool nextIf(StringRef str) { if (!Text.substr(Pos).starts_with(str)) return false; Pos += str.size(); return true; } char peekChar() { if (Pos >= Text.size()) return 0; return Text[Pos]; } char nextChar() { if (Pos >= Text.size()) return 0; return Text[Pos++]; } bool nextIf(char c) { if (peekChar() != c) return false; Pos++; return true; } void pushBack() { assert(Pos > 0); Pos--; } StringRef consumeAll() { StringRef str = Text.drop_front(Pos); Pos = Text.size(); return str; } void pushNode(NodePointer Nd) { NodeStack.push_back(Nd, *this); } NodePointer popNode() { return NodeStack.pop_back_val(); } NodePointer popNode(Node::Kind kind) { if (NodeStack.empty()) return nullptr; Node::Kind NdKind = NodeStack.back()->getKind(); if (NdKind != kind) return nullptr; return popNode(); } template NodePointer popNode(Pred pred) { if (NodeStack.empty()) return nullptr; Node::Kind NdKind = NodeStack.back()->getKind(); if (!pred(NdKind)) return nullptr; return popNode(); } /// This class handles preparing the initial state for a demangle job in a reentrant way, pushing the /// existing state back when a demangle job is completed. class DemangleInitRAII { Demangler &Dem; Vector NodeStack; Vector Substitutions; int NumWords; StringRef Text; size_t Pos; std::function SymbolicReferenceResolver; public: DemangleInitRAII(Demangler &Dem, StringRef MangledName, std::function SymbolicReferenceResolver); ~DemangleInitRAII(); }; friend DemangleInitRAII; void addSubstitution(NodePointer Nd) { if (Nd) { Substitutions.push_back(Nd, *this); } } NodePointer addChild(NodePointer Parent, NodePointer Child); NodePointer createWithChild(Node::Kind kind, NodePointer Child); NodePointer createType(NodePointer Child); NodePointer createWithChildren(Node::Kind kind, NodePointer Child1, NodePointer Child2); NodePointer createWithChildren(Node::Kind kind, NodePointer Child1, NodePointer Child2, NodePointer Child3); NodePointer createWithChildren(Node::Kind kind, NodePointer Child1, NodePointer Child2, NodePointer Child3, NodePointer Child4); NodePointer createWithPoppedType(Node::Kind kind) { return createWithChild(kind, popNode(Node::Kind::Type)); } bool parseAndPushNodes(); NodePointer changeKind(NodePointer Node, Node::Kind NewKind); NodePointer demangleOperator(); int demangleNatural(); int demangleIndex(); NodePointer demangleIndexAsNode(); NodePointer demangleDependentConformanceIndex(); NodePointer demangleIdentifier(); NodePointer demangleOperatorIdentifier(); std::string demangleBridgedMethodParams(); NodePointer demangleMultiSubstitutions(); NodePointer pushMultiSubstitutions(int RepeatCount, size_t SubstIdx); NodePointer createSwiftType(Node::Kind typeKind, const char *name); NodePointer demangleStandardSubstitution(); NodePointer createStandardSubstitution(char Subst, bool SecondLevel); NodePointer demangleLocalIdentifier(); NodePointer popModule(); NodePointer popContext(); NodePointer popTypeAndGetChild(); NodePointer popTypeAndGetAnyGeneric(); NodePointer demangleBuiltinType(); NodePointer demangleAnyGenericType(Node::Kind kind); NodePointer demangleExtensionContext(); NodePointer demanglePlainFunction(); NodePointer popFunctionType(Node::Kind kind, bool hasClangType = false); NodePointer popFunctionParams(Node::Kind kind); NodePointer popFunctionParamLabels(NodePointer FuncType); NodePointer popTuple(); NodePointer popTypeList(); NodePointer popPack(); NodePointer popSILPack(); NodePointer popProtocol(); NodePointer demangleBoundGenericType(); NodePointer demangleBoundGenericArgs(NodePointer nominalType, const Vector &TypeLists, size_t TypeListIdx); NodePointer popAnyProtocolConformanceList(); NodePointer popRetroactiveConformances(); NodePointer demangleRetroactiveConformance(); NodePointer demangleInitializer(); NodePointer demangleImplParamConvention(Node::Kind ConvKind); NodePointer demangleImplResultConvention(Node::Kind ConvKind); NodePointer demangleImplParameterSending(); NodePointer demangleImplParameterIsolated(); NodePointer demangleImplParameterImplicitLeading(); NodePointer demangleImplParameterResultDifferentiability(); NodePointer demangleImplFunctionType(); NodePointer demangleClangType(); NodePointer demangleMetatype(); NodePointer demanglePrivateContextDescriptor(); NodePointer createArchetypeRef(int depth, int i); NodePointer demangleArchetype(); NodePointer demangleAssociatedTypeSimple(NodePointer GenericParamIdx); NodePointer demangleAssociatedTypeCompound(NodePointer GenericParamIdx); NodePointer demangleExtendedExistentialShape(char kind); NodePointer demangleSymbolicExtendedExistentialType(); NodePointer popAssocTypeName(); NodePointer popAssocTypePath(); NodePointer getDependentGenericParamType(int depth, int index); NodePointer demangleGenericParamIndex(); NodePointer popProtocolConformance(); NodePointer demangleRetroactiveProtocolConformanceRef(); NodePointer popAnyProtocolConformance(); NodePointer demangleConcreteProtocolConformance(); NodePointer demanglePackProtocolConformance(); NodePointer popDependentProtocolConformance(); NodePointer demangleDependentProtocolConformanceRoot(); NodePointer demangleDependentProtocolConformanceInherited(); NodePointer popDependentAssociatedConformance(); NodePointer demangleDependentProtocolConformanceAssociated(); NodePointer demangleDependentProtocolConformanceOpaque(); NodePointer demangleThunkOrSpecialization(); NodePointer demangleGenericSpecialization(Node::Kind SpecKind, NodePointer droppedArguments); NodePointer demangleGenericSpecializationWithDroppedArguments(); NodePointer demangleFunctionSpecialization(); NodePointer demangleFuncSpecParam(Node::Kind Kind); NodePointer addFuncSpecParamNumber(NodePointer Param, FunctionSigSpecializationParamKind Kind); NodePointer demangleSpecAttributes(Node::Kind SpecKind); NodePointer demangleWitness(); NodePointer demangleSpecialType(); NodePointer demangleMetatypeRepresentation(); NodePointer demangleAccessor(NodePointer ChildNode); NodePointer demangleFunctionEntity(); NodePointer demangleEntity(Node::Kind Kind); NodePointer demangleVariable(); NodePointer demangleSubscript(); NodePointer demangleProtocolList(); NodePointer demangleProtocolListType(); NodePointer demangleGenericSignature(bool hasParamCounts); NodePointer demangleGenericRequirement(); NodePointer demangleGenericType(); NodePointer demangleValueWitness(); NodePointer demangleMacroExpansion(); NodePointer demangleTypeMangling(); NodePointer demangleSymbolicReference(unsigned char rawKind); NodePointer demangleTypeAnnotation(); NodePointer demangleAutoDiffFunctionOrSimpleThunk(Node::Kind nodeKind); NodePointer demangleAutoDiffFunctionKind(); NodePointer demangleAutoDiffSubsetParametersThunk(); NodePointer demangleAutoDiffSelfReorderingReabstractionThunk(); NodePointer demangleDifferentiabilityWitness(); NodePointer demangleIndexSubset(); NodePointer demangleDifferentiableFunctionType(); NodePointer demangleConstrainedExistentialRequirementList(); bool demangleBoundGenerics(Vector &TypeListList, NodePointer &RetroactiveConformances); NodePointer demangleIntegerType(); void dump(); public: Demangler() {} void clear() override; /// Demangle the given symbol and return the parse tree. /// /// \param MangledName The mangled symbol string, which start with the /// mangling prefix $S. /// \param SymbolicReferenceResolver A function invoked to resolve symbolic references in /// the string. If null, then symbolic references will cause the demangle to fail. /// /// \returns A parse tree for the demangled string - or a null pointer /// on failure. /// The lifetime of the returned node tree ends with the lifetime of the /// Demangler or with a call of clear(). NodePointer demangleSymbol(StringRef MangledName, std::function SymbolicReferenceResolver = nullptr); /// Demangle the given type and return the parse tree. /// /// \param MangledName The mangled type string, which does _not_ start with /// the mangling prefix $S. /// \param SymbolicReferenceResolver A function invoked to resolve symbolic references in /// the string. If null, then symbolic references will cause the demangle to fail. /// /// \returns A parse tree for the demangled string - or a null pointer /// on failure. /// The lifetime of the returned node tree ends with the lifetime of the /// Demangler or with a call of clear(). NodePointer demangleType(StringRef MangledName, std::function SymbolicReferenceResolver = nullptr); }; /// A demangler which uses stack space for its initial memory. /// /// The \p Size parameter specifies the size of the stack space. template class StackAllocatedDemangler : public Demangler { char StackSpace[Size]; public: StackAllocatedDemangler() { providePreallocatedMemory(StackSpace, Size); } }; NodePointer demangleOldSymbolAsNode(StringRef MangledName, NodeFactory &Factory); SWIFT_END_INLINE_NAMESPACE } // end namespace Demangle } // end namespace swift #endif // SWIFT_DEMANGLING_DEMANGLER_H