/* * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2021, Jerome Duval, jerome.duval@gmail.com. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #include #ifdef _KERNEL_MODE # include #endif #include "demangle.h" // C++ ABI: https://itanium-cxx-abi.github.io/cxx-abi/abi.html //#define TRACE_GCC3_DEMANGLER #ifdef TRACE_GCC3_DEMANGLER # define TRACE(x...) PRINT(x) # define DEBUG_SCOPE(name) DebugScope debug(name, fInput.String()) #else # define TRACE(x...) ; # define DEBUG_SCOPE(name) do {} while (false) #endif #ifdef _KERNEL_MODE # define PRINT(format...) kprintf(format) # define VPRINT(format, args) PRINT("%s", format) // no vkprintf() # define NEW(constructor) new(kdebug_alloc) constructor # define DELETE(object) DebugAlloc::destroy(object) #else # define PRINT(format...) printf(format) # define VPRINT(format, args) vprintf(format, args) # define NEW(constructor) new(std::nothrow) constructor # define DELETE(object) delete object #endif typedef long number_type; enum { ERROR_OK = 0, ERROR_NOT_MANGLED, ERROR_UNSUPPORTED, ERROR_INVALID, ERROR_BUFFER_TOO_SMALL, ERROR_NO_MEMORY, ERROR_INTERNAL, ERROR_INVALID_PARAMETER_INDEX }; // object classification enum object_type { OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_DATA, OBJECT_TYPE_FUNCTION, OBJECT_TYPE_METHOD_CLASS, OBJECT_TYPE_METHOD_OBJECT, OBJECT_TYPE_METHOD_UNKNOWN }; // prefix classification enum prefix_type { PREFIX_NONE, PREFIX_NAMESPACE, PREFIX_CLASS, PREFIX_UNKNOWN }; // type classification enum type_type { TYPE_ELLIPSIS, TYPE_VOID, TYPE_WCHAR_T, TYPE_BOOL, TYPE_CHAR, TYPE_SIGNED_CHAR, TYPE_UNSIGNED_CHAR, TYPE_SHORT, TYPE_UNSIGNED_SHORT, TYPE_INT, TYPE_UNSIGNED_INT, TYPE_LONG, TYPE_UNSIGNED_LONG, TYPE_LONG_LONG, TYPE_UNSIGNED_LONG_LONG, TYPE_INT128, TYPE_UNSIGNED_INT128, TYPE_FLOAT, TYPE_DOUBLE, TYPE_LONG_DOUBLE, TYPE_FLOAT128, TYPE_DFLOAT16, TYPE_DFLOAT32, TYPE_DFLOAT64, TYPE_DFLOAT128, TYPE_CHAR16_T, TYPE_CHAR32_T, TYPE_UNKNOWN, TYPE_CONST_CHAR_POINTER, TYPE_POINTER, TYPE_REFERENCE }; const char* const kTypeNames[] = { "...", "void", "wchar_t", "bool", "char", "signed char", "unsigned char", "short", "unsigned short", "int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long", "__int128", "unsigned __int128", "float", "double", "long double", "__float128", "__dfloat16", // TODO: Official names for the __dfloat*! "__dfloat32", "__dfloat64", "__dfloat64", "char16_t", "char32_t", "?", "char const*", "void*", "void&" }; // CV qualifier flags enum { CV_QUALIFIER_RESTRICT = 0x1, CV_QUALIFIER_VOLATILE = 0x2, CV_QUALIFIER_CONST = 0x4 }; enum type_modifier { TYPE_QUALIFIER_POINTER = 0, TYPE_QUALIFIER_REFERENCE, TYPE_QUALIFIER_RVALUE_REFERENCE, TYPE_QUALIFIER_COMPLEX, TYPE_QUALIFIER_IMAGINARY }; static const char* const kTypeModifierSuffixes[] = { "*", "&", "&&", " complex", " imaginary" }; struct operator_info { const char* mangled_name; const char* name; int argument_count; int flags; }; // operator flags enum { OPERATOR_TYPE_PARAM = 0x01, OPERATOR_IS_MEMBER = 0x02 }; static const operator_info kOperatorInfos[] = { { "nw", "new", -1, OPERATOR_IS_MEMBER }, { "na", "new[]", -1, OPERATOR_IS_MEMBER }, { "dl", "delete", -1, OPERATOR_IS_MEMBER }, { "da", "delete[]", -1, OPERATOR_IS_MEMBER }, { "ps", "+", 1, 0 }, // unary { "ng", "-", 1, 0 }, // unary { "ad", "&", 1, 0 }, // unary { "de", "*", 1, 0 }, // unary { "co", "~", 1, 0 }, { "pl", "+", 2, 0 }, { "mi", "-", 2, 0 }, { "ml", "*", 2, 0 }, { "dv", "/", 2, 0 }, { "rm", "%", 2, 0 }, { "an", "&", 2, 0 }, { "or", "|", 2, 0 }, { "eo", "^", 2, 0 }, { "aS", "=", 2, 0 }, { "pL", "+=", 2, 0 }, { "mI", "-=", 2, 0 }, { "mL", "*=", 2, 0 }, { "dV", "/=", 2, 0 }, { "rM", "%=", 2, 0 }, { "aN", "&=", 2, 0 }, { "oR", "|=", 2, 0 }, { "eO", "^=", 2, 0 }, { "ls", "<<", 2, 0 }, { "rs", ">>", 2, 0 }, { "lS", "<<=", 2, 0 }, { "rS", ">>=", 2, 0 }, { "eq", "==", 2, 0 }, { "ne", "!=", 2, 0 }, { "lt", "<", 2, 0 }, { "gt", ">", 2, 0 }, { "le", "<=", 2, 0 }, { "ge", ">=", 2, 0 }, { "nt", "!", 1, 0 }, { "aa", "&&", 2, 0 }, { "oo", "||", 2, 0 }, { "pp", "++", 1, 0 }, { "mm", "--", 1, 0 }, { "cm", ",", -1, 0 }, { "pm", "->*", 2, 0 }, { "pt", "->", 2, 0 }, { "cl", "()", -1, 0 }, { "ix", "[]", -1, 0 }, { "qu", "?", 3, 0 }, { "st", "sizeof", 1, OPERATOR_TYPE_PARAM }, // type { "sz", "sizeof", 1, 0 }, // expression { "at", "alignof", 1, OPERATOR_TYPE_PARAM }, // type { "az", "alignof", 1, 0 }, // expression {} }; #ifdef TRACE_GCC3_DEMANGLER struct DebugScope { DebugScope(const char* functionName, const char* remainingString = NULL) : fParent(sGlobalScope), fFunctionName(functionName), fLevel(fParent != NULL ? fParent->fLevel + 1 : 0) { sGlobalScope = this; if (remainingString != NULL) { PRINT("%*s%s(): \"%s\"\n", fLevel * 2, "", fFunctionName, remainingString); } else PRINT("%*s%s()\n", fLevel * 2, "", fFunctionName); } ~DebugScope() { sGlobalScope = fParent; PRINT("%*s%s() done\n", fLevel * 2, "", fFunctionName); } static void Print(const char* format,...) { int level = sGlobalScope != NULL ? sGlobalScope->fLevel : 0; va_list args; va_start(args, format); PRINT("%*s", (level + 1) * 2, ""); VPRINT(format, args); va_end(args); } private: DebugScope* fParent; const char* fFunctionName; int fLevel; static DebugScope* sGlobalScope; }; DebugScope* DebugScope::sGlobalScope = NULL; #endif // TRACE_GCC3_DEMANGLER class Input { public: Input() : fString(NULL), fLength(0) { } void SetTo(const char* string, size_t length) { fString = string; fLength = length; } const char* String() const { return fString; } int CharsRemaining() const { return fLength; } void Skip(size_t count) { if (count > fLength) { PRINT("Input::Skip(): fOffset > fLength\n"); return; } fString += count; fLength -= count; } bool HasPrefix(char prefix) const { return fLength > 0 && fString[0] == prefix; } bool HasPrefix(const char* prefix) const { size_t prefixLen = strlen(prefix); return prefixLen <= fLength && strncmp(fString, prefix, strlen(prefix)) == 0; } bool SkipPrefix(char prefix) { if (!HasPrefix(prefix)) return false; fString++; fLength--; return true; } bool SkipPrefix(const char* prefix) { size_t prefixLen = strlen(prefix); if (prefixLen <= fLength && strncmp(fString, prefix, prefixLen) != 0) return false; fString += prefixLen; fLength -= prefixLen; return true; } char operator[](size_t index) const { if (index >= fLength) { PRINT("Input::operator[](): fOffset + index >= fLength\n"); return '\0'; } return fString[index]; } private: const char* fString; size_t fLength; }; class NameBuffer { public: NameBuffer(char* buffer, size_t size) : fBuffer(buffer), fSize(size), fLength(0), fOverflow(false) { } bool IsEmpty() const { return fLength == 0; } char LastChar() const { return fLength > 0 ? fBuffer[fLength - 1] : '\0'; } bool HadOverflow() const { return fOverflow; } char* Terminate() { fBuffer[fLength] = '\0'; return fBuffer; } bool Append(const char* string, size_t length) { if (fLength + length >= fSize) { fOverflow = true; return false; } memcpy(fBuffer + fLength, string, length); fLength += length; return true; } bool Append(const char* string) { return Append(string, strlen(string)); } private: char* fBuffer; size_t fSize; size_t fLength; bool fOverflow; }; struct TypeInfo { type_type type; int cvQualifiers; TypeInfo() : type(TYPE_UNKNOWN), cvQualifiers(0) { } TypeInfo(type_type type) : type(type), cvQualifiers(0) { } TypeInfo(const TypeInfo& other, int cvQualifiers = 0) : type(other.type), cvQualifiers(other.cvQualifiers | cvQualifiers) { } TypeInfo& operator=(const TypeInfo& other) { type = other.type; cvQualifiers = other.cvQualifiers; return *this; } }; struct DemanglingParameters { bool objectNameOnly; DemanglingParameters(bool objectNameOnly) : objectNameOnly(objectNameOnly) { } }; struct DemanglingInfo : DemanglingParameters { object_type objectType; DemanglingInfo(bool objectNameOnly) : DemanglingParameters(objectNameOnly), objectType(OBJECT_TYPE_UNKNOWN) { } }; struct ParameterInfo { TypeInfo type; ParameterInfo() { } }; class Node; struct NameDecorationInfo { const Node* firstDecorator; const Node* closestCVDecoratorList; NameDecorationInfo(const Node* decorator) : firstDecorator(decorator), closestCVDecoratorList(NULL) { } }; struct CVQualifierInfo { const Node* firstCVQualifier; const Node* firstNonCVQualifier; CVQualifierInfo() : firstCVQualifier(NULL), firstNonCVQualifier(NULL) { } }; class Node { public: Node() : fNextAllocated(NULL), fParent(NULL), fNext(NULL), fNextReferenceable(NULL), fReferenceable(true) { } virtual ~Node() { } Node* NextAllocated() const { return fNextAllocated; } void SetNextAllocated(Node* node) { fNextAllocated = node; } Node* Parent() const { return fParent; } virtual void SetParent(Node* node) { fParent = node; } Node* Next() const { return fNext; } void SetNext(Node* node) { fNext = node; } bool IsReferenceable() const { return fReferenceable; } void SetReferenceable(bool flag) { fReferenceable = flag; } Node* NextReferenceable() const { return fNextReferenceable; } void SetNextReferenceable(Node* node) { fNextReferenceable = node; } virtual bool GetName(NameBuffer& buffer) const = 0; virtual bool GetDecoratedName(NameBuffer& buffer, NameDecorationInfo& decorationInfo) const { if (!GetName(buffer)) return false; return decorationInfo.firstDecorator == NULL || decorationInfo.firstDecorator->AddDecoration(buffer, NULL); } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { return true; } virtual void GetCVQualifierInfo(CVQualifierInfo& info) const { info.firstNonCVQualifier = this; } virtual Node* GetUnqualifiedNode(Node* beforeNode) { return this; } virtual bool IsTemplatized() const { return false; } virtual Node* TemplateParameterAt(int index) const { return NULL; } virtual bool IsNoReturnValueFunction() const { return false; } virtual bool IsTypeName(const char* name, size_t length) const { return false; } virtual object_type ObjectType() const { return OBJECT_TYPE_UNKNOWN; } virtual prefix_type PrefixType() const { return PREFIX_NONE; } virtual TypeInfo Type() const { return TypeInfo(); } private: Node* fNextAllocated; Node* fParent; Node* fNext; Node* fNextReferenceable; bool fReferenceable; }; class NamedTypeNode : public Node { public: NamedTypeNode(Node* name) : fName(name) { if (fName != NULL) fName->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { return fName == NULL || fName->GetName(buffer); } virtual bool IsNoReturnValueFunction() const { return fName != NULL && fName->IsNoReturnValueFunction(); } virtual TypeInfo Type() const { return fName != NULL ? fName->Type() : TypeInfo(); } protected: Node* fName; }; class SubstitutionNode : public Node { public: SubstitutionNode(Node* node) : fNode(node) { } virtual bool GetName(NameBuffer& buffer) const { return fNode->GetName(buffer); } virtual bool GetDecoratedName(NameBuffer& buffer, NameDecorationInfo& decorationInfo) const { return fNode->GetDecoratedName(buffer, decorationInfo); } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { return fNode->AddDecoration(buffer, stopDecorator); } virtual void GetCVQualifierInfo(CVQualifierInfo& info) const { fNode->GetCVQualifierInfo(info); } virtual bool IsTemplatized() const { return fNode->IsTemplatized(); } virtual Node* TemplateParameterAt(int index) const { return fNode->TemplateParameterAt(index); } virtual bool IsNoReturnValueFunction() const { return fNode->IsNoReturnValueFunction(); } virtual bool IsTypeName(const char* name, size_t length) const { return fNode->IsTypeName(name, length); } virtual object_type ObjectType() const { return fNode->ObjectType(); } virtual prefix_type PrefixType() const { return fNode->PrefixType(); } virtual TypeInfo Type() const { return fNode->Type(); } private: Node* fNode; }; class ArrayNode : public NamedTypeNode { public: ArrayNode(Node* type, int dimension) : NamedTypeNode(type), fDimensionExpression(NULL), fDimensionNumber(dimension) { } ArrayNode(Node* type, Node* dimension) : NamedTypeNode(type), fDimensionExpression(dimension), fDimensionNumber(0) { fDimensionExpression->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { if (!fName->GetName(buffer)) return false; buffer.Append("[", 1); if (fDimensionExpression != NULL) { if (!fDimensionExpression->GetName(buffer)) return false; } else { char stringBuffer[16]; snprintf(stringBuffer, sizeof(stringBuffer), "%d", fDimensionNumber); buffer.Append(stringBuffer); } return buffer.Append("]", 1); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } virtual TypeInfo Type() const { // TODO: Check! return TypeInfo(TYPE_POINTER); } private: Node* fDimensionExpression; int fDimensionNumber; }; class ObjectNode : public NamedTypeNode { public: ObjectNode(Node* name) : NamedTypeNode(name) { } virtual bool GetObjectName(NameBuffer& buffer, const DemanglingParameters& parameters) { if (parameters.objectNameOnly) return fName != NULL ? fName->GetName(buffer) : true; return GetName(buffer); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } virtual Node* ParameterAt(uint32 index) const { return NULL; } }; class SimpleNameNode : public Node { public: SimpleNameNode(const char* name) : fName(name), fLength(strlen(name)) { } SimpleNameNode(const char* name, size_t length) : fName(name), fLength(length) { } virtual bool GetName(NameBuffer& buffer) const { return buffer.Append(fName, fLength); } protected: const char* fName; size_t fLength; }; class SimpleTypeNode : public SimpleNameNode { public: SimpleTypeNode(const char* name) : SimpleNameNode(name), fType(TYPE_UNKNOWN) { } SimpleTypeNode(type_type type) : SimpleNameNode(kTypeNames[type]), fType(type) { } virtual bool IsTypeName(const char* name, size_t length) const { return fLength == length && strcmp(fName, name) == 0; } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } virtual TypeInfo Type() const { return TypeInfo(fType); } private: type_type fType; }; class TypedNumberLiteralNode : public Node { public: TypedNumberLiteralNode(Node* type, const char* number, size_t length) : fType(type), fNumber(number), fLength(length) { fType->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { // If the type is bool and the number is 0 or 1, we use "false" or // "true" respectively. if (fType->IsTypeName("bool", 4) && fLength == 1 && (fNumber[0] == '0' || fNumber[0] == '1')) { return buffer.Append(fNumber[0] == '0' ? "false" : "true"); } // Add the type in parentheses. The GNU demangler omits "int", so do we. if (!fType->IsTypeName("int", 3)) { buffer.Append("("); if (!fType->GetName(buffer)) return false; buffer.Append(")"); } // add the number -- replace a leading 'n' by '-', if necessary if (fLength > 0 && fNumber[0] == 'n') { buffer.Append("-"); return buffer.Append(fNumber + 1, fLength - 1); } return buffer.Append(fNumber, fLength); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } private: Node* fType; const char* fNumber; size_t fLength; }; class XtructorNode : public Node { public: XtructorNode(bool constructor, char type) : fConstructor(constructor), fType(type) { } virtual void SetParent(Node* node) { fUnqualifiedNode = node->GetUnqualifiedNode(this); Node::SetParent(node); } virtual bool GetName(NameBuffer& buffer) const { if (fUnqualifiedNode == NULL) return false; if (!fConstructor) buffer.Append("~"); return fUnqualifiedNode->GetName(buffer); } virtual bool IsNoReturnValueFunction() const { return true; } virtual object_type ObjectType() const { return OBJECT_TYPE_METHOD_CLASS; } private: bool fConstructor; char fType; Node* fUnqualifiedNode; }; class SpecialNameNode : public Node { public: SpecialNameNode(const char* name, Node* child) : fName(name), fChild(child) { fChild->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { return buffer.Append(fName) && fChild->GetName(buffer); } protected: const char* fName; Node* fChild; }; class DecoratingNode : public Node { public: DecoratingNode(Node* child) : fChildNode(child) { fChildNode->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { NameDecorationInfo decorationInfo(this); return fChildNode->GetDecoratedName(buffer, decorationInfo); } virtual bool GetDecoratedName(NameBuffer& buffer, NameDecorationInfo& decorationInfo) const { decorationInfo.closestCVDecoratorList = NULL; return fChildNode->GetDecoratedName(buffer, decorationInfo); } protected: Node* fChildNode; }; class CVQualifiersNode : public DecoratingNode { public: CVQualifiersNode(int qualifiers, Node* child) : DecoratingNode(child), fCVQualifiers(qualifiers) { } virtual bool GetDecoratedName(NameBuffer& buffer, NameDecorationInfo& decorationInfo) const { if (decorationInfo.closestCVDecoratorList == NULL) decorationInfo.closestCVDecoratorList = this; return fChildNode->GetDecoratedName(buffer, decorationInfo); } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { if (this == stopDecorator) return true; if (!fChildNode->AddDecoration(buffer, stopDecorator)) return false; if ((fCVQualifiers & CV_QUALIFIER_RESTRICT) != 0) buffer.Append(" restrict"); if ((fCVQualifiers & CV_QUALIFIER_VOLATILE) != 0) buffer.Append(" volatile"); if ((fCVQualifiers & CV_QUALIFIER_CONST) != 0) buffer.Append(" const"); return true; } virtual void GetCVQualifierInfo(CVQualifierInfo& info) const { if (info.firstCVQualifier == NULL) info.firstCVQualifier = this; fChildNode->GetCVQualifierInfo(info); } virtual bool IsTemplatized() const { return fChildNode->IsTemplatized(); } virtual Node* TemplateParameterAt(int index) const { return fChildNode->TemplateParameterAt(index); } virtual bool IsNoReturnValueFunction() const { return fChildNode->IsNoReturnValueFunction(); } virtual object_type ObjectType() const { return fChildNode->ObjectType(); } virtual prefix_type PrefixType() const { return fChildNode->PrefixType(); } virtual TypeInfo Type() const { return TypeInfo(fChildNode->Type(), fCVQualifiers); } private: int fCVQualifiers; }; class TypeModifierNode : public DecoratingNode { public: TypeModifierNode(type_modifier modifier, Node* child) : DecoratingNode(child), fModifier(modifier) { } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { if (this == stopDecorator) return true; return fChildNode->AddDecoration(buffer, stopDecorator) && buffer.Append(kTypeModifierSuffixes[fModifier]); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } virtual TypeInfo Type() const { TypeInfo type = fChildNode->Type(); if (type.type == TYPE_CHAR && (type.cvQualifiers & CV_QUALIFIER_CONST) != 0) { return TypeInfo(TYPE_CONST_CHAR_POINTER); } switch (fModifier) { case TYPE_QUALIFIER_POINTER: return TypeInfo(TYPE_POINTER); case TYPE_QUALIFIER_REFERENCE: return TypeInfo(TYPE_REFERENCE); default: return TypeInfo(); } } private: type_modifier fModifier; }; class VendorTypeModifierNode : public DecoratingNode { public: VendorTypeModifierNode(Node* name, Node* child) : DecoratingNode(child), fName(name) { fName->SetParent(this); } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { if (this == stopDecorator) return true; return fChildNode->AddDecoration(buffer, stopDecorator) && buffer.Append(" ") && fName->GetName(buffer); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } private: Node* fName; }; class OperatorNode : public Node { public: OperatorNode(const operator_info* info) : fInfo(info) { SetReferenceable(false); } virtual bool GetName(NameBuffer& buffer) const { return buffer.Append( isalpha(fInfo->name[0]) ? "operator " : "operator") && buffer.Append(fInfo->name); } virtual object_type ObjectType() const { return (fInfo->flags & OPERATOR_IS_MEMBER) != 0 ? OBJECT_TYPE_METHOD_CLASS : OBJECT_TYPE_UNKNOWN; } private: const operator_info* fInfo; }; class VendorOperatorNode : public Node { public: VendorOperatorNode(Node* name) : fName(name) { fName->SetParent(this); SetReferenceable(false); } virtual bool GetName(NameBuffer& buffer) const { return buffer.Append("operator ") && fName->GetName(buffer); } private: Node* fName; }; class CastOperatorNode : public Node { public: CastOperatorNode(Node* child) : fChildNode(child) { fChildNode->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { return buffer.Append("operator ") && fChildNode->GetName(buffer); } virtual bool IsNoReturnValueFunction() const { return true; } virtual object_type ObjectType() const { return OBJECT_TYPE_METHOD_OBJECT; } private: Node* fChildNode; }; class PrefixedNode : public Node { public: PrefixedNode(Node* prefix, Node* node) : fPrefixNode(prefix), fNode(node) { fPrefixNode->SetParent(this); fNode->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { if (!fPrefixNode->GetName(buffer)) return false; buffer.Append("::"); return fNode->GetName(buffer); } virtual Node* GetUnqualifiedNode(Node* beforeNode) { return beforeNode == fNode ? fPrefixNode->GetUnqualifiedNode(beforeNode) : fNode->GetUnqualifiedNode(beforeNode); } virtual bool IsNoReturnValueFunction() const { return fNode->IsNoReturnValueFunction(); } virtual object_type ObjectType() const { return fNode->ObjectType(); } virtual prefix_type PrefixType() const { return PREFIX_UNKNOWN; } private: Node* fPrefixNode; Node* fNode; }; class ClonedNode : public ObjectNode { public: ClonedNode(Node* clone, ObjectNode* node) : ObjectNode(NULL), fNode(node), fCloneNode(clone) { fNode->SetParent(this); fCloneNode->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { if (!fNode->GetName(buffer)) return false; buffer.Append(" ", 1); return _AppendCloneName(buffer); } virtual bool GetObjectName(NameBuffer& buffer, const DemanglingParameters& parameters) { if (parameters.objectNameOnly) { if (!fNode->GetObjectName(buffer, parameters)) return false; if (!_AppendCloneName(buffer)) return false; return buffer.Append(" ", 1); } return ObjectNode::GetObjectName(buffer, parameters); } virtual Node* GetUnqualifiedNode(Node* beforeNode) { return beforeNode == fCloneNode ? fNode->GetUnqualifiedNode(beforeNode) : fCloneNode->GetUnqualifiedNode(beforeNode); } virtual bool IsNoReturnValueFunction() const { return fNode->IsNoReturnValueFunction(); } virtual object_type ObjectType() const { return fNode->ObjectType(); } virtual prefix_type PrefixType() const { return PREFIX_UNKNOWN; } virtual Node* ParameterAt(uint32 index) const { return fNode->ParameterAt(index); } private: bool _AppendCloneName(NameBuffer& buffer) const { buffer.Append("[clone "); if (!fCloneNode->GetName(buffer)) return false; buffer.Append("]"); return true; } private: ObjectNode* fNode; Node* fCloneNode; }; typedef PrefixedNode DependentNameNode; class TemplateNode : public Node { public: TemplateNode(Node* base) : fBase(base), fFirstArgument(NULL), fLastArgument(NULL) { fBase->SetParent(this); } void AddArgument(Node* child) { child->SetParent(this); if (fLastArgument != NULL) { fLastArgument->SetNext(child); fLastArgument = child; } else { fFirstArgument = child; fLastArgument = child; } } virtual bool GetName(NameBuffer& buffer) const { if (!fBase->GetName(buffer)) return false; buffer.Append("<"); Node* child = fFirstArgument; while (child != NULL) { if (child != fFirstArgument) buffer.Append(", "); if (!child->GetName(buffer)) return false; child = child->Next(); } // add a space between consecutive '>' if (buffer.LastChar() == '>') buffer.Append(" "); return buffer.Append(">"); } virtual Node* GetUnqualifiedNode(Node* beforeNode) { return fBase != beforeNode ? fBase->GetUnqualifiedNode(beforeNode) : this; } virtual bool IsTemplatized() const { return true; } virtual Node* TemplateParameterAt(int index) const { Node* child = fFirstArgument; while (child != NULL) { if (index == 0) return child; index--; child = child->Next(); } return NULL; } virtual bool IsNoReturnValueFunction() const { return fBase->IsNoReturnValueFunction(); } virtual object_type ObjectType() const { return fBase->ObjectType(); } virtual prefix_type PrefixType() const { return fBase->PrefixType(); } protected: Node* fBase; Node* fFirstArgument; Node* fLastArgument; }; class MultiSubExpressionsNode : public Node { public: MultiSubExpressionsNode() : fFirstSubExpression(NULL), fLastSubExpression(NULL) { } void AddSubExpression(Node* child) { child->SetParent(this); if (fLastSubExpression != NULL) { fLastSubExpression->SetNext(child); fLastSubExpression = child; } else { fFirstSubExpression = child; fLastSubExpression = child; } } protected: Node* fFirstSubExpression; Node* fLastSubExpression; }; class CallNode : public MultiSubExpressionsNode { public: CallNode() { } virtual bool GetName(NameBuffer& buffer) const { // TODO: Use the real syntax! buffer.Append("call("); Node* child = fFirstSubExpression; while (child != NULL) { if (child != fFirstSubExpression) buffer.Append(", "); if (!child->GetName(buffer)) return false; child = child->Next(); } buffer.Append(")"); return true; } }; class OperatorExpressionNode : public MultiSubExpressionsNode { public: OperatorExpressionNode(const operator_info* info) : fInfo(info) { } virtual bool GetName(NameBuffer& buffer) const { bool isIdentifier = isalpha(fInfo->name[0]) || fInfo->name[0] == '_'; if (fInfo->argument_count == 1 || isIdentifier || fInfo->argument_count > 3 || (fInfo->argument_count == 3 && strcmp(fInfo->name, "?") != 0)) { // prefix operator buffer.Append(fInfo->name); if (isIdentifier) buffer.Append("("); Node* child = fFirstSubExpression; while (child != NULL) { if (child != fFirstSubExpression) buffer.Append(", "); if (!child->GetName(buffer)) return false; child = child->Next(); } if (isIdentifier) buffer.Append(")"); return true; } Node* arg1 = fFirstSubExpression; Node* arg2 = arg1->Next(); buffer.Append("("); if (fInfo->argument_count == 2) { // binary infix operator if (!arg1->GetName(buffer)) return false; buffer.Append(" "); buffer.Append(fInfo->name); buffer.Append(" "); if (!arg2->GetName(buffer)) return false; return buffer.Append(")"); } Node* arg3 = arg2->Next(); if (fInfo->argument_count == 2) { // trinary operator "... ? ... : ..." if (!arg1->GetName(buffer)) return false; buffer.Append(" ? "); if (!arg2->GetName(buffer)) return false; buffer.Append(" : "); if (!arg3->GetName(buffer)) return false; return buffer.Append(")"); } return false; } private: const operator_info* fInfo; }; class ConversionExpressionNode : public MultiSubExpressionsNode { public: ConversionExpressionNode(Node* type) : fType(type) { fType->SetParent(this); } virtual bool GetName(NameBuffer& buffer) const { buffer.Append("("); if (!fType->GetName(buffer)) return false; buffer.Append(")("); Node* child = fFirstSubExpression; while (child != NULL) { if (child != fFirstSubExpression) buffer.Append(", "); if (!child->GetName(buffer)) return false; child = child->Next(); } return buffer.Append(")"); } private: Node* fType; }; class PointerToMemberNode : public DecoratingNode { public: PointerToMemberNode(Node* classType, Node* memberType) : DecoratingNode(memberType), fClassType(classType) { fClassType->SetParent(this); } virtual bool AddDecoration(NameBuffer& buffer, const Node* stopDecorator) const { if (this == stopDecorator) return true; if (!fChildNode->AddDecoration(buffer, stopDecorator)) return false; // In most cases we need a space before the name. In some it is // superfluous, though. if (!buffer.IsEmpty() && buffer.LastChar() != '(') buffer.Append(" "); if (!fClassType->GetName(buffer)) return false; return buffer.Append("::*"); } virtual object_type ObjectType() const { return OBJECT_TYPE_DATA; } virtual TypeInfo Type() const { // TODO: Method pointers aren't ordinary pointers. Though we might not // be able to determine the difference. return TypeInfo(TYPE_POINTER); } private: Node* fClassType; }; class FunctionNode : public ObjectNode { public: FunctionNode(Node* nameNode, bool hasReturnType, bool isExternC) : ObjectNode(nameNode), fFirstTypeNode(NULL), fLastTypeNode(NULL), fHasReturnType(hasReturnType), fIsExternC(isExternC) { } void AddType(Node* child) { child->SetParent(this); if (fLastTypeNode != NULL) { fLastTypeNode->SetNext(child); fLastTypeNode = child; } else { fFirstTypeNode = child; fLastTypeNode = child; } } virtual bool GetName(NameBuffer& buffer) const { NameDecorationInfo decorationInfo(NULL); return GetDecoratedName(buffer, decorationInfo); } virtual bool GetDecoratedName(NameBuffer& buffer, NameDecorationInfo& decorationInfo) const { // write 'extern "C"' // if (fIsExternC) // buffer.Append("extern \"C\""); // write the return type Node* child = fFirstTypeNode; if (_HasReturnType() && child != NULL) { if (!child->GetName(buffer)) return false; child = child->Next(); buffer.Append(" ", 1); } // write the function name if (fName == NULL) buffer.Append("(", 1); CVQualifierInfo info; if (fName != NULL) { // skip CV qualifiers on our name -- we'll add them later fName->GetCVQualifierInfo(info); if (info.firstNonCVQualifier != NULL && !info.firstNonCVQualifier->GetName(buffer)) { return false; } } // add non-CV qualifier decorations if (decorationInfo.firstDecorator != NULL) { if (!decorationInfo.firstDecorator->AddDecoration(buffer, decorationInfo.closestCVDecoratorList)) { return false; } } if (fName == NULL) buffer.Append(")", 1); // add the parameter types buffer.Append("("); // don't add a single "void" parameter if (child != NULL && child->Next() == NULL && child->IsTypeName("void", 4)) { child = NULL; } Node* firstParam = child; while (child != NULL) { if (child != firstParam) buffer.Append(", "); if (!child->GetName(buffer)) return false; child = child->Next(); } buffer.Append(")"); // add CV qualifiers on our name if (info.firstCVQualifier != NULL) { if (!info.firstCVQualifier->AddDecoration(buffer, info.firstNonCVQualifier)) { return false; } } // add CV qualifiers on us if (decorationInfo.closestCVDecoratorList != NULL) decorationInfo.closestCVDecoratorList->AddDecoration(buffer, NULL); return true; } virtual object_type ObjectType() const { // no name, no fun if (fName == NULL) return OBJECT_TYPE_FUNCTION; // check our name's prefix switch (fName->PrefixType()) { case PREFIX_NONE: case PREFIX_NAMESPACE: return OBJECT_TYPE_FUNCTION; case PREFIX_CLASS: case PREFIX_UNKNOWN: break; } // Our name has a prefix, but we don't know, whether it is a class or // namespace. Let's ask our name what it thinks it is. object_type type = fName->ObjectType(); switch (type) { case OBJECT_TYPE_FUNCTION: case OBJECT_TYPE_METHOD_CLASS: case OBJECT_TYPE_METHOD_OBJECT: case OBJECT_TYPE_METHOD_UNKNOWN: // That's as good as it gets. return type; case OBJECT_TYPE_UNKNOWN: case OBJECT_TYPE_DATA: default: // Obviously our name doesn't have a clue. return OBJECT_TYPE_METHOD_UNKNOWN; } } virtual Node* ParameterAt(uint32 index) const { // skip return type Node* child = fFirstTypeNode; if (_HasReturnType() && child != NULL) child = child->Next(); // ignore a single "void" parameter if (child != NULL && child->Next() == NULL && child->IsTypeName("void", 4)) { return NULL; } // get the type at the index while (child != NULL && index > 0) { child = child->Next(); index--; } return child; } private: bool _HasReturnType() const { return fHasReturnType || fName == NULL || (fName->IsTemplatized() && !fName->IsNoReturnValueFunction()); } private: Node* fFirstTypeNode; Node* fLastTypeNode; bool fHasReturnType; bool fIsExternC; }; // #pragma mark - Demangler class Demangler { public: Demangler(); int Demangle(const char* mangledName, char* buffer, size_t size, DemanglingInfo& demanglingInfo); int GetParameterInfo(const char* mangledName, uint32 index, char* buffer, size_t size, ParameterInfo& info); // actually private, but public to make gcc 2 happy inline bool _SetError(int error); inline void _AddAllocatedNode(Node* node); private: template struct NodeCreator; inline bool _SkipExpected(char c); inline bool _SkipExpected(const char* string); void _Init(); void _Cleanup(); int _Demangle(const char* mangledName, char* buffer, size_t size, DemanglingInfo& demanglingInfo); int _GetParameterInfo(const char* mangledName, uint32 index, char* buffer, size_t size, ParameterInfo& info); int _Parse(const char* mangledName, const char*& versionSuffix, ObjectNode*& _node); bool _ParseClone(ObjectNode*& _node); bool _ParseEncoding(ObjectNode*& _node); bool _ParseSpecialName(Node*& _node); bool _ParseCallOffset(bool& nonVirtual, number_type& offset1, number_type& offset2); bool _ParseName(Node*& _node); bool _ParseNestedName(Node*& _node); bool _ParseNestedNameInternal(Node*& _node); bool _ParseLocalName(Node*& _node); bool _ParseUnqualifiedName(Node*& _node); bool _ParseSourceName(Node*& _node); bool _ParseOperatorName(Node*& _node); bool _ParseType(Node*& _node); bool _ParseTypeInternal(Node*& _node); void _ParseCVQualifiers(int& qualifiers); bool _ParseTypeWithModifier(type_modifier modifier, int toSkip, Node*& _node); bool _TryParseBuiltinType(Node*& _node); bool _ParseFunctionType(FunctionNode*& _node);; bool _ParseArrayType(Node*& _node); bool _ParsePointerToMemberType(Node*& _node); bool _ParseTemplateParam(Node*& _node); bool _ParseSubstitution(Node*& _node); bool _ParseSubstitutionInternal(Node*& _node); bool _ParseBareFunctionType(FunctionNode* node); bool _ParseTemplateArgs(Node* node, Node*& _node); bool _ParseTemplateArg(Node*& _node); bool _ParseExpression(Node*& _node); bool _ParseExpressionPrimary(Node*& _node); bool _ParseNumber(number_type& number); bool _CreateNodeAndSkip(const char* name, size_t length, int toSkip, Node*& _node); bool _CreateNodeAndSkip(const char* name, int toSkip, Node*& _node); bool _CreateTypeNodeAndSkip(type_type type, int toSkip, Node*& _node); bool _CreateTypeNodeAndSkip(const char* name, const char* prefix, const char* templateArgs, int toSkip, Node*& _node); void _RegisterReferenceableNode(Node* node); bool _CreateSubstitutionNode(int index, Node*& _node); private: Input fInput; int fError; Node* fAllocatedNodes; Node* fFirstReferenceableNode; Node* fLastReferenceableNode; Node* fTemplatizedNode; }; template struct Demangler::NodeCreator { NodeCreator(Demangler* demangler) : fDemangler(demangler) { } template inline bool operator()(ReturnType*& _node) const { _node = NEW(NodeType); if (_node == NULL) return fDemangler->_SetError(ERROR_NO_MEMORY); fDemangler->_AddAllocatedNode(_node); return true; } template inline bool operator()(ParameterType1 arg1, ReturnType*& _node) const { _node = NEW(NodeType(arg1)); if (_node == NULL) return fDemangler->_SetError(ERROR_NO_MEMORY); fDemangler->_AddAllocatedNode(_node); return true; } template inline bool operator()(ParameterType1 arg1, ParameterType2 arg2, ReturnType*& _node) const { _node = NEW(NodeType(arg1, arg2)); if (_node == NULL) return fDemangler->_SetError(ERROR_NO_MEMORY); fDemangler->_AddAllocatedNode(_node); return true; } template inline bool operator()(ParameterType1 arg1, ParameterType2 arg2, ParameterType3 arg3, ReturnType*& _node) const { _node = NEW(NodeType(arg1, arg2, arg3)); if (_node == NULL) return fDemangler->_SetError(ERROR_NO_MEMORY); fDemangler->_AddAllocatedNode(_node); return true; } private: Demangler* fDemangler; }; inline bool Demangler::_SetError(int error) { if (fError == ERROR_OK) { fError = error; #ifdef TRACE_GCC3_DEMANGLER DebugScope::Print("_SetError(): %d, remaining input: \"%s\"\n", error, fInput.String()); #endif } return false; } inline void Demangler::_AddAllocatedNode(Node* node) { node->SetNextAllocated(fAllocatedNodes); fAllocatedNodes = node; } inline bool Demangler::_SkipExpected(char c) { return fInput.SkipPrefix(c) || _SetError(ERROR_INVALID); } inline bool Demangler::_SkipExpected(const char* string) { return fInput.SkipPrefix(string) || _SetError(ERROR_INVALID); } Demangler::Demangler() : fInput(), fAllocatedNodes(NULL) { } int Demangler::Demangle(const char* mangledName, char* buffer, size_t size, DemanglingInfo& demanglingInfo) { DEBUG_SCOPE("Demangle"); _Init(); int result = _Demangle(mangledName, buffer, size, demanglingInfo); _Cleanup(); return result; } int Demangler::GetParameterInfo(const char* mangledName, uint32 index, char* buffer, size_t size, ParameterInfo& info) { DEBUG_SCOPE("GetParameterInfo"); _Init(); int result = _GetParameterInfo(mangledName, index, buffer, size, info); _Cleanup(); return result; } void Demangler::_Init() { fError = ERROR_OK; fFirstReferenceableNode = NULL; fLastReferenceableNode = NULL; fAllocatedNodes = NULL; fTemplatizedNode = NULL; } void Demangler::_Cleanup() { while (fAllocatedNodes != NULL) { Node* node = fAllocatedNodes; fAllocatedNodes = node->NextAllocated(); DELETE(node); } } int Demangler::_Demangle(const char* mangledName, char* buffer, size_t size, DemanglingInfo& demanglingInfo) { // parse the name const char* versionSuffix; ObjectNode* node; int error = _Parse(mangledName, versionSuffix, node); if (error != ERROR_OK) return error; NameBuffer nameBuffer(buffer, size); bool success = node->GetObjectName(nameBuffer, demanglingInfo); // If versioned, append the unmodified version string if (success && versionSuffix != NULL) nameBuffer.Append(versionSuffix); if (nameBuffer.HadOverflow()) return ERROR_BUFFER_TOO_SMALL; if (!success) return ERROR_INTERNAL; demanglingInfo.objectType = node->ObjectType(); nameBuffer.Terminate(); return ERROR_OK; } int Demangler::_GetParameterInfo(const char* mangledName, uint32 index, char* buffer, size_t size, ParameterInfo& info) { // parse the name const char* versionSuffix; ObjectNode* node; int error = _Parse(mangledName, versionSuffix, node); if (error != ERROR_OK) return error; // get the parameter node Node* parameter = node->ParameterAt(index); if (parameter == NULL) return ERROR_INVALID_PARAMETER_INDEX; // get the parameter name NameBuffer nameBuffer(buffer, size); bool success = parameter->GetName(nameBuffer); if (nameBuffer.HadOverflow()) return ERROR_BUFFER_TOO_SMALL; if (!success) return ERROR_INTERNAL; nameBuffer.Terminate(); // get the type info.type = parameter->Type(); return ERROR_OK; } int Demangler::_Parse(const char* mangledName, const char*& versionSuffix, ObjectNode*& _node) { // To support versioned symbols, we ignore the version suffix when // demangling. versionSuffix = strchr(mangledName, '@'); fInput.SetTo(mangledName, versionSuffix != NULL ? versionSuffix - mangledName : strlen(mangledName)); // ::= _Z []* if (!fInput.SkipPrefix("_Z")) return ERROR_NOT_MANGLED; if (!_ParseEncoding(_node) || !_ParseClone(_node)) return fError; if (fInput.CharsRemaining() != 0) { // bogus at end of input return ERROR_INVALID; } return ERROR_OK; } bool Demangler::_ParseClone(ObjectNode*& _node) { DEBUG_SCOPE("_ParseClone"); while (fInput.HasPrefix('.')) { int count = fInput.CharsRemaining(); int i = 1; while (true) { for (; i < count && fInput[i] != '.'; i++) ; if (i + 1 >= count || fInput[i + 1] < '0' || fInput[i + 1] > '9') break; i++; } if (i == 1) break; Node* clone; if (!_CreateNodeAndSkip(fInput.String(), i, i, clone)) return false; if (!NodeCreator(this)(clone, _node, _node)) return false; } return true; } bool Demangler::_ParseEncoding(ObjectNode*& _node) { DEBUG_SCOPE("_ParseEncoding"); // ::= // ::= // ::= // NOTE: This is not in the specs: Local entities seem to be prefixed // by an 'L'. fInput.SkipPrefix('L'); // parse , if it is one Node* name; if (fInput.HasPrefix('T') || fInput.HasPrefix("GV")) { return _ParseSpecialName(name) && NodeCreator(this)(name, _node); } // either or if (!_ParseName(name)) return false; if (fInput.CharsRemaining() == 0 || fInput.HasPrefix('E') || fInput.HasPrefix('.')) { // return NodeCreator(this)(name, _node); } // -- parse remaining FunctionNode* functionNode; if (!NodeCreator(this)(name, false, false, functionNode)) return false; _node = functionNode; // If our name is templatized, we push it onto the templatized node // stack while parsing the function parameters. Node* previousTemplatizedNode = fTemplatizedNode; if (name->IsTemplatized()) fTemplatizedNode = name; if (!_ParseBareFunctionType(functionNode)) return false; fTemplatizedNode = previousTemplatizedNode; return true; } bool Demangler::_ParseSpecialName(Node*& _node) { DEBUG_SCOPE("_ParseSpecialName"); if (fInput.CharsRemaining() == 0) return _SetError(ERROR_INVALID); // ::= GV # Guard variable for one-time // # initialization // # No if (!fInput.SkipPrefix('T')) { Node* name; return _SkipExpected("GV") && _ParseName(name) && NodeCreator(this)("guard variable for ", name, _node); } // ::= TV # virtual table // ::= TT # VTT structure (construction vtable // # index) // ::= TI # typeinfo structure // ::= TS # typeinfo name (null-terminated byte // # string) const char* prefix = NULL; switch (fInput[0]) { case 'V': prefix = "vtable for "; break; case 'T': prefix = "VTT for "; break; case 'I': prefix = "typeinfo for "; break; case 'S': prefix = "typeinfo name for "; break; } if (prefix != NULL) { fInput.Skip(1); Node* type; return _ParseType(type) && NodeCreator(this)(prefix, type, _node); } // ::= Tc // # base is the nominal target function of thunk // # first call-offset is 'this' adjustment // # second call-offset is result adjustment if (fInput.SkipPrefix('c')) { bool nonVirtual; number_type offset1; number_type offset2; ObjectNode* name; return _ParseCallOffset(nonVirtual, offset1, offset2) && _ParseCallOffset(nonVirtual, offset1, offset2) && _ParseEncoding(name) && NodeCreator(this)( "covariant return thunk to ", name, _node); } // ::= T // # base is the nominal target function of thunk bool nonVirtual; number_type offset1; number_type offset2; ObjectNode* name; return _ParseCallOffset(nonVirtual, offset1, offset2) && _ParseEncoding(name) && NodeCreator(this)( nonVirtual ? "non-virtual thunk to " : "virtual thunk to ", name, _node); } bool Demangler::_ParseCallOffset(bool& nonVirtual, number_type& offset1, number_type& offset2) { // ::= h _ // ::= v _ // ::= // # non-virtual base override // ::= _ // # virtual base override, with vcall offset // non-virtual if (fInput.SkipPrefix('h')) { nonVirtual = true; return _ParseNumber(offset1) && _SkipExpected('_'); } // virtual nonVirtual = false; return _SkipExpected('v') && _ParseNumber(offset1) && _SkipExpected('_') && _ParseNumber(offset2) && _SkipExpected('_'); } bool Demangler::_ParseName(Node*& _node) { DEBUG_SCOPE("_ParseName"); if (fInput.CharsRemaining() == 0) return _SetError(ERROR_INVALID); // ::= // ::= // ::= // ::= # See Scope Encoding below // // ::= // ::= St # ::std:: // // ::= // ::= switch (fInput[0]) { case 'N': // return _ParseNestedName(_node); case 'Z': // return _ParseLocalName(_node); case 'S': { // if (!fInput.HasPrefix("St")) { if (!_ParseSubstitution(_node)) return false; break; } // std:: namespace fInput.Skip(2); Node* prefix; if (!NodeCreator(this)("std", prefix)) return false; // Node* node; if (!_ParseUnqualifiedName(node) || !NodeCreator(this)(prefix, node, _node)) { return false; } break; } default: // if (!_ParseUnqualifiedName(_node)) return false; break; } // We get here for the names that might be an . // Check whether are following. if (!fInput.HasPrefix('I')) return true; // is referenceable _RegisterReferenceableNode(_node); return _ParseTemplateArgs(_node, _node); } bool Demangler::_ParseNestedName(Node*& _node) { DEBUG_SCOPE("_ParseNestedName"); // ::= N [] E // ::= N [] // E // // ::= [r] [V] [K] # restrict (C99), volatile, const // // ::= // ::= // ::= // ::= # empty // ::= // // ::=