// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "common.h" #include "knowntype.h" #include #include #include class AbstractSymbolGroupNode; class SymbolGroupNode; class SymbolGroup; class MemoryHandle; struct SymbolGroupValueContext { SymbolGroupValueContext(CIDebugDataSpaces *ds, CIDebugSymbols *s) : dataspaces(ds), symbols(s) {} SymbolGroupValueContext() = default; CIDebugDataSpaces *dataspaces = nullptr; CIDebugSymbols *symbols = nullptr; }; struct SymbolAncestorInfo { bool isValid() const { return offset >= 0 && !type.empty(); } std::string type; LONG64 offset = -1; }; class SymbolGroupValue { explicit SymbolGroupValue(const std::string &parentError); public: typedef std::pair Symbol; typedef std::list SymbolList; explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c); SymbolGroupValue(); explicit operator bool() const { return isValid(); } bool isValid() const; // Access children by name or index (0-based) SymbolGroupValue operator[](const char *name) const; SymbolGroupValue operator[](unsigned) const; unsigned childCount() const; ULONG64 offsetOfChild(const SymbolGroupValue &child) const; SymbolGroupValue parent() const; // take address and cast to desired (pointer) type SymbolGroupValue typeCast(const char *type) const; // take pointer value and cast to desired (pointer) type SymbolGroupValue pointerTypeCast(const char *type) const; // Find a member variable traversing the list of base classes. This useful // for skipping template base classes of STL containers whose number varies // by MSVC version. static SymbolGroupValue findMember(const SymbolGroupValue &start, const std::string &symbolName); std::string name() const; std::string type() const; std::vector innerTypes() const { return innerTypesOf(type()); } std::wstring value() const; unsigned size() const; //offset based access to ancestors SymbolGroupValue addSymbolForAncestor(const std::string &ancestorName) const; ULONG64 readPointerValueFromAncestor(const std::string &name) const; int readIntegerFromAncestor(const std::string &name, int defaultValue = -1) const; LONG64 offsetOfAncestor(const std::string &name) const; ULONG64 addressOfAncestor(const std::string &name) const; std::string typeOfAncestor(const std::string &childName) const; SymbolAncestorInfo infoOfAncestor(const std::string &name) const; SymbolGroupValue addSymbol(const ULONG64 address, const std::string &type) const; SymbolGroupNode *node() const { return m_node; } SymbolGroupValueContext context() const { return m_context; } int intValue(int defaultValue = -1) const; double floatValue(double defaultValue = -999) const; ULONG64 pointerValue(ULONG64 defaultValue = 0) const; ULONG64 address() const; std::string module() const; // Return allocated array of data pointed to unsigned char *pointerData(unsigned length) const; // Return data pointed to as wchar_t/std::wstring (UTF16) std::wstring wcharPointerData(unsigned charCount, unsigned maxCharCount = 512) const; std::string error() const; // Some helpers for manipulating types. static unsigned sizeOf(const char *type); // Offset of structure field: "!moduleQMapNode", "value". static unsigned fieldOffset(const char *type, const char *field); static std::string stripPointerType(const std::string &); // Strip "class ", "struct " static std::string stripClassPrefixes(const std::string &); // Strip " const" from "char const*", (map key), "QString const", etc. // which otherwise causes GetTypeSize to fail. static std::string stripConst(const std::string &type); static std::string addPointerType(const std::string &); static std::string stripArrayType(const std::string &); static std::string stripModuleFromType(const std::string &type); static std::string moduleOfType(const std::string &type); // pointer type, return number of characters to strip static unsigned isPointerType(const std::string &); static unsigned isMovable(const std::string &, const SymbolGroupValue &v); static bool isArrayType(const std::string &); static bool isVTableType(const std::string &t); // add pointer type 'Foo' -> 'Foo *', 'Foo *' -> 'Foo **' static std::string pointerType(const std::string &type); // Symbol Name/(Expression) of a pointed-to instance ('Foo' at 0x10') ==> '*(Foo *)0x10' static std::string pointedToSymbolName(ULONG64 address, const std::string &type); // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString') // Some operations on types (like adding symbols may fail non-deterministically // or be slow when the module specification is omitted). // If a current SymbolGroup is passed on, its module will be used for templates. static std::string resolveType(const std::string &type, const SymbolGroupValueContext &ctx, const std::string ¤tModule = std::string()); static std::list resolveSymbolName(const char *pattern, const SymbolGroupValueContext &c, std::string *errorMessage = 0); static SymbolList resolveSymbol(const char *pattern, const SymbolGroupValueContext &c, std::string *errorMessage = 0); static std::list getAllModuleNames(const SymbolGroupValueContext &c, std::string *errorMessage = 0); static unsigned char *readMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage = 0); static ULONG64 readPointerValue(CIDebugDataSpaces *ds, ULONG64 address, std::string *errorMessage = 0); static ULONG64 readUnsignedValue(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize, ULONG64 defaultValue = 0, std::string *errorMessage = 0); static LONG64 readSignedValue(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize, LONG64 defaultValue = 0, std::string *errorMessage = 0); static int readIntValue(CIDebugDataSpaces *ds, ULONG64 address, int defaultValue = 0, std::string *errorMessage = 0); static double readDouble(CIDebugDataSpaces *ds, ULONG64 address, double defaultValue = 0.0, std::string *errorMessage = 0); static bool writeMemory(CIDebugDataSpaces *ds, ULONG64 address, const unsigned char *data, ULONG length, std::string *errorMessage = 0); static unsigned pointerSize(); static unsigned pointerDiffSize(); static unsigned intSize(); // get the inner types: "QMap" -> "int", "double" static std::vector innerTypesOf(const std::string &t); static unsigned verbose; private: bool ensureExpanded() const; SymbolGroupValue typeCastedValue(ULONG64 address, const char *type) const; template POD readPODFromAncestor(const std::string &name, POD defaultValue) const; SymbolGroupNode *m_node = 0; SymbolGroupValueContext m_context; mutable std::string m_errorMessage; }; // For debugging purposes std::ostream &operator<<(std::ostream &, const SymbolGroupValue &v); struct QtInfo { enum Module { Core, Gui, Widgets, Network, Script, Qml }; static const QtInfo &get(const SymbolGroupValueContext &ctx); // Prepend core module and Qt namespace. To be able to work with some // 'complicated' types like QMapNode, specifying the module helps std::string prependQtModule(const std::string &type, Module m = Core) const { return QtInfo::prependModuleAndNameSpace(type, moduleName(m), nameSpace); } std::string prependQtCoreModule(const std::string &type) const { return prependQtModule(type, Core); } std::string prependQtGuiModule(const std::string &type) const { return QtInfo::prependQtModule(type, Gui); } std::string prependQtWidgetsModule(const std::string &type) const { return QtInfo::prependQtModule(type, Widgets); } std::string prependQtNetworkModule(const std::string &type) const { return QtInfo::prependQtModule(type, Network); } std::string prependQtScriptModule(const std::string &type) const { return QtInfo::prependQtModule(type, Script); } // Prepend module and namespace if missing with some smartness // ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo' static std::string prependModuleAndNameSpace(const std::string &type, const std::string &module, const std::string &nameSpace); static int qtTypeInfoVersion(const SymbolGroupValueContext &ctx); std::string moduleName(Module m) const; int version = 0; bool isStatic = false; std::string nameSpace; std::string libInfix; // Fully qualified types with module and namespace std::string qObjectType; std::string qObjectPrivateType; std::string qWindowPrivateType; std::string qWidgetPrivateType; }; std::ostream &operator<<(std::ostream &os, const QtInfo &); /* Helpers for detecting types reported from IDebugSymbolGroup * 1) Class prefix==true is applicable to outer types obtained from * from IDebugSymbolGroup: 'class foo' or 'struct foo'. * 2) Class prefix==false is for inner types of templates, etc, doing * a more expensive check: 'foo' */ enum { KnownTypeHasClassPrefix = 0x1, // Strip "class Foo" -> "Foo" KnownTypeAutoStripPointer = 0x2 // Strip "class Foo *" -> "Foo" for conveniently // handling the pointer/value duality of symbol group entries }; KnownType knownType(const std::string &type, unsigned flags); // Debug helper void formatKnownTypeFlags(std::ostream &os, KnownType kt); // Dump builtin simple types using SymbolGroupValue expressions, // returning SymbolGroupNode dumper flags. Might return special info // (for example, a cached additional node) for some types to be used in // complex dumpers unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s, std::string *encoding, int *knownType = 0, int *containerSizeIn = 0, void **specialInfoIn = 0, MemoryHandle **memoryHandleIn = 0); void dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &, const std::string &desiredFormat, std::ostream &str); enum AssignEncoding { AssignPlainValue, AssignHexEncoded, AssignHexEncodedUtf16 }; bool assignType(SymbolGroupNode *n, int knownType, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage); // Non-container complex dumpers (QObjects/QVariants). std::vector dumpComplexType(SymbolGroupNode *node, int type, void *specialInfo, const SymbolGroupValueContext &ctx);