diff options
| author | Glenn Smith <glenn@vector35.com> | 2023-11-06 15:28:59 -0500 |
|---|---|---|
| committer | Glenn Smith <glenn@vector35.com> | 2023-11-06 16:10:47 -0500 |
| commit | 6436615567547349434351468012512505109552 (patch) | |
| tree | e798973c25805377731c4a199f40379263bc128c | |
| parent | 4108964609171327618627477163339656037111 (diff) | |
Type Browser
| -rw-r--r-- | binaryninjaapi.h | 10 | ||||
| -rw-r--r-- | binaryninjacore.h | 24 | ||||
| -rw-r--r-- | binaryview.cpp | 102 | ||||
| -rw-r--r-- | python/binaryview.py | 74 | ||||
| -rw-r--r-- | python/types.py | 5 | ||||
| -rw-r--r-- | suite/api_test.py | 2 | ||||
| -rw-r--r-- | type.cpp | 4 | ||||
| -rw-r--r-- | ui/action.h | 1 | ||||
| -rw-r--r-- | ui/render.h | 5 | ||||
| -rw-r--r-- | ui/theme.h | 1 | ||||
| -rw-r--r-- | ui/tokenizedtextwidget.h | 254 | ||||
| -rw-r--r-- | ui/typebrowser.h | 524 | ||||
| -rw-r--r-- | ui/typeeditor.h | 168 | ||||
| -rw-r--r-- | ui/util.h | 3 | ||||
| -rw-r--r-- | ui/viewframe.h | 1 |
15 files changed, 1173 insertions, 5 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d32af5ce..021069e5 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -30,6 +30,7 @@ #include <vector> #include <map> #include <unordered_map> +#include <unordered_set> #include <exception> #include <functional> #include <set> @@ -3169,7 +3170,7 @@ namespace BinaryNinja { { BNTypeDefinitionLineType lineType; std::vector<InstructionTextToken> tokens; - Ref<Type> type, rootType; + Ref<Type> type, parentType, rootType; std::string rootTypeName; Ref<NamedTypeReference> baseType; uint64_t baseOffset; @@ -4505,6 +4506,13 @@ namespace BinaryNinja { */ std::vector<Confidence<Ref<Type>>> GetTypesReferenced(const QualifiedName& type, uint64_t offset); + std::unordered_set<QualifiedName> GetOutgoingDirectTypeReferences(const QualifiedName& type); + std::unordered_set<QualifiedName> GetOutgoingRecursiveTypeReferences(const QualifiedName& type); + std::unordered_set<QualifiedName> GetOutgoingRecursiveTypeReferences(const std::unordered_set<QualifiedName>& types); + std::unordered_set<QualifiedName> GetIncomingDirectTypeReferences(const QualifiedName& type); + std::unordered_set<QualifiedName> GetIncomingRecursiveTypeReferences(const QualifiedName& type); + std::unordered_set<QualifiedName> GetIncomingRecursiveTypeReferences(const std::unordered_set<QualifiedName>& types); + Ref<Structure> CreateStructureBasedOnFieldAccesses(const QualifiedName& type); // Unimplemented! /*! Returns a list of virtual addresses called by the call site in the ReferenceSource diff --git a/binaryninjacore.h b/binaryninjacore.h index 6f46326a..995f2f4f 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1630,6 +1630,7 @@ extern "C" PaddingLineType, UndefinedXrefLineType, CollapsedPaddingLineType, + EmptyLineType, } BNTypeDefinitionLineType; typedef struct BNTypeDefinitionLine @@ -1638,6 +1639,7 @@ extern "C" BNInstructionTextToken* tokens; size_t count; BNType* type; + BNType* parentType; BNType* rootType; char* rootTypeName; BNNamedTypeReference* baseType; @@ -1868,7 +1870,22 @@ extern "C" BraceOption3Color, BraceOption4Color, BraceOption5Color, - BraceOption6Color + BraceOption6Color, + + // Type class colors + VoidTypeColor, + StructureTypeColor, + EnumerationTypeColor, + FunctionTypeColor, + BoolTypeColor, + IntegerTypeColor, + FloatTypeColor, + PointerTypeColor, + ArrayTypeColor, + VarArgsTypeColor, + ValueTypeColor, + NamedTypeReferenceColor, + WideCharTypeColor, } BNThemeColor; // The following edge styles map to Qt's Qt::PenStyle enumeration @@ -4127,6 +4144,11 @@ extern "C" BINARYNINJACOREAPI BNTypeWithConfidence* BNGetTypesReferenced( BNBinaryView* view, BNQualifiedName* type, uint64_t offset, size_t* count); + BINARYNINJACOREAPI BNQualifiedName* BNGetOutgoingDirectTypeReferences(BNBinaryView* view, BNQualifiedName* type, size_t* count); + BINARYNINJACOREAPI BNQualifiedName* BNGetOutgoingRecursiveTypeReferences(BNBinaryView* view, BNQualifiedName* types, size_t typeCount, size_t* count); + BINARYNINJACOREAPI BNQualifiedName* BNGetIncomingDirectTypeReferences(BNBinaryView* view, BNQualifiedName* type, size_t* count); + BINARYNINJACOREAPI BNQualifiedName* BNGetIncomingRecursiveTypeReferences(BNBinaryView* view, BNQualifiedName* types, size_t typeCount, size_t* count); + BINARYNINJACOREAPI void BNRegisterGlobalFunctionRecognizer(BNFunctionRecognizer* rec); BINARYNINJACOREAPI bool BNGetStringAtAddress(BNBinaryView* view, uint64_t addr, BNStringReference* strRef); diff --git a/binaryview.cpp b/binaryview.cpp index 4bbd3aa3..077f2b4a 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -2491,6 +2491,108 @@ std::vector<Confidence<Ref<Type>>> BinaryView::GetTypesReferenced(const Qualifie } +unordered_set<QualifiedName> BinaryView::GetOutgoingDirectTypeReferences(const QualifiedName& type) +{ + size_t count; + BNQualifiedName apiType = type.GetAPIObject(); + BNQualifiedName* apiResult = BNGetOutgoingDirectTypeReferences(m_object, &apiType, &count); + QualifiedName::FreeAPIObject(&apiType); + if (!apiResult) + return {}; + + unordered_set<QualifiedName> result; + for (size_t i = 0; i < count; i ++) + { + result.insert(QualifiedName::FromAPIObject(&apiResult[i])); + } + BNFreeTypeNameList(apiResult, count); + return result; +} + + +unordered_set<QualifiedName> BinaryView::GetOutgoingRecursiveTypeReferences(const QualifiedName& type) +{ + return GetOutgoingRecursiveTypeReferences(unordered_set<QualifiedName>{type}); +} + + +unordered_set<QualifiedName> BinaryView::GetOutgoingRecursiveTypeReferences(const unordered_set<QualifiedName>& types) +{ + size_t count; + vector<BNQualifiedName> apiTypes; + for (auto& type: types) + { + apiTypes.push_back(type.GetAPIObject()); + } + BNQualifiedName* apiResult = BNGetOutgoingRecursiveTypeReferences(m_object, apiTypes.data(), apiTypes.size(), &count); + for (auto& type: apiTypes) + { + QualifiedName::FreeAPIObject(&type); + } + if (!apiResult) + return {}; + + unordered_set<QualifiedName> result; + for (size_t i = 0; i < count; i ++) + { + result.insert(QualifiedName::FromAPIObject(&apiResult[i])); + } + BNFreeTypeNameList(apiResult, count); + return result; +} + + +unordered_set<QualifiedName> BinaryView::GetIncomingDirectTypeReferences(const QualifiedName& type) +{ + size_t count; + BNQualifiedName apiType = type.GetAPIObject(); + BNQualifiedName* apiResult = BNGetIncomingDirectTypeReferences(m_object, &apiType, &count); + QualifiedName::FreeAPIObject(&apiType); + if (!apiResult) + return {}; + + unordered_set<QualifiedName> result; + for (size_t i = 0; i < count; i ++) + { + result.insert(QualifiedName::FromAPIObject(&apiResult[i])); + } + BNFreeTypeNameList(apiResult, count); + return result; +} + + +unordered_set<QualifiedName> BinaryView::GetIncomingRecursiveTypeReferences(const QualifiedName& type) +{ + return GetIncomingRecursiveTypeReferences(unordered_set<QualifiedName>{type}); +} + + +unordered_set<QualifiedName> BinaryView::GetIncomingRecursiveTypeReferences(const unordered_set<QualifiedName>& types) +{ + size_t count; + vector<BNQualifiedName> apiTypes; + for (auto& type: types) + { + apiTypes.push_back(type.GetAPIObject()); + } + BNQualifiedName* apiResult = BNGetIncomingRecursiveTypeReferences(m_object, apiTypes.data(), apiTypes.size(), &count); + for (auto& type: apiTypes) + { + QualifiedName::FreeAPIObject(&type); + } + if (!apiResult) + return {}; + + unordered_set<QualifiedName> result; + for (size_t i = 0; i < count; i ++) + { + result.insert(QualifiedName::FromAPIObject(&apiResult[i])); + } + BNFreeTypeNameList(apiResult, count); + return result; +} + + vector<uint64_t> BinaryView::GetCallees(ReferenceSource callSite) { size_t count; diff --git a/python/binaryview.py b/python/binaryview.py index d7d2a2a8..de434074 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -4979,6 +4979,80 @@ class BinaryView: finally: core.BNFreeTypeFieldReferenceTypes(refs, count.value) + def get_outgoing_direct_type_references(self, name: '_types.QualifiedNameType') -> List['_types.QualifiedName']: + qname = _types.QualifiedName(name) + _qname = qname._to_core_struct() + count = ctypes.c_ulonglong(0) + _result = core.BNGetOutgoingDirectTypeReferences(self.handle, _qname, count) + assert _result is not None, "core.BNGetOutgoingDirectTypeReferences returned None" + try: + result = [] + for i in range(0, count.value): + result_name = _types.QualifiedName._from_core_struct(_result[i]) + result.append(result_name) + return result + finally: + core.BNFreeTypeNameList(_result, count.value) + + def get_outgoing_recursive_type_references(self, names: Union['_types.QualifiedNameType', List['_types.QualifiedNameType']]) -> List['_types.QualifiedName']: + qnames = [] + if isinstance(names, list): + for name in names: + qnames.append(_types.QualifiedName(name)) + else: + qnames.append(_types.QualifiedName(names)) + _qnames = (core.BNQualifiedName * len(qnames))() + for i, qname in enumerate(qnames): + _qnames[i] = qname._to_core_struct() + count = ctypes.c_ulonglong(0) + _result = core.BNGetOutgoingRecursiveTypeReferences(self.handle, _qnames, len(qnames), count) + assert _result is not None, "core.BNGetOutgoingRecursiveTypeReferences returned None" + try: + result = [] + for i in range(0, count.value): + result_name = _types.QualifiedName._from_core_struct(_result[i]) + result.append(result_name) + return result + finally: + core.BNFreeTypeNameList(_result, count.value) + + def get_incoming_direct_type_references(self, name: '_types.QualifiedNameType') -> List['_types.QualifiedName']: + qname = _types.QualifiedName(name) + _qname = qname._to_core_struct() + count = ctypes.c_ulonglong(0) + _result = core.BNGetIncomingDirectTypeReferences(self.handle, _qname, count) + assert _result is not None, "core.BNGetIncomingDirectTypeReferences returned None" + try: + result = [] + for i in range(0, count.value): + result_name = _types.QualifiedName._from_core_struct(_result[i]) + result.append(result_name) + return result + finally: + core.BNFreeTypeNameList(_result, count.value) + + def get_incoming_recursive_type_references(self, names: Union['_types.QualifiedNameType', List['_types.QualifiedNameType']]) -> List['_types.QualifiedName']: + qnames = [] + if isinstance(names, list): + for name in names: + qnames.append(_types.QualifiedName(name)) + else: + qnames.append(_types.QualifiedName(names)) + _qnames = (core.BNQualifiedName * len(qnames))() + for i, qname in enumerate(qnames): + _qnames[i] = qname._to_core_struct() + count = ctypes.c_ulonglong(0) + _result = core.BNGetIncomingRecursiveTypeReferences(self.handle, _qnames, len(qnames), count) + assert _result is not None, "core.BNGetIncomingRecursiveTypeReferences returned None" + try: + result = [] + for i in range(0, count.value): + result_name = _types.QualifiedName._from_core_struct(_result[i]) + result.append(result_name) + return result + finally: + core.BNFreeTypeNameList(_result, count.value) + def create_structure_from_offset_access(self, name: '_types.QualifiedName') -> '_types.StructureType': newMemberAdded = ctypes.c_bool(False) _name = _types.QualifiedName(name)._to_core_struct() diff --git a/python/types.py b/python/types.py index 9fbe7a93..68858ac8 100644 --- a/python/types.py +++ b/python/types.py @@ -237,6 +237,7 @@ class TypeDefinitionLine: line_type: TypeDefinitionLineType tokens: List['_function.InstructionTextToken'] type: 'Type' + parent_type: 'Type' root_type: 'Type' root_type_name: str base_type: Optional['NamedTypeReferenceType'] @@ -254,6 +255,7 @@ class TypeDefinitionLine: def _from_core_struct(struct: core.BNTypeDefinitionLine, platform: Optional[_platform.Platform] = None): tokens = _function.InstructionTextToken._from_core_struct(struct.tokens, struct.count) type_ = Type.create(handle=core.BNNewTypeReference(struct.type), platform=platform) + parent_type = Type.create(handle=core.BNNewTypeReference(struct.parentType), platform=platform) root_type = Type.create(handle=core.BNNewTypeReference(struct.rootType), platform=platform) root_type_name = core.pyNativeStr(struct.rootTypeName) if struct.baseType: @@ -263,7 +265,7 @@ class TypeDefinitionLine: base_type = NamedTypeReferenceType(handle, platform) else: base_type = None - return TypeDefinitionLine(struct.lineType, tokens, type_, root_type, root_type_name, base_type, + return TypeDefinitionLine(struct.lineType, tokens, type_, parent_type, root_type, root_type_name, base_type, struct.baseOffset, struct.offset, struct.fieldIndex) def _to_core_struct(self): @@ -272,6 +274,7 @@ class TypeDefinitionLine: struct.tokens = _function.InstructionTextToken._get_core_struct(self.tokens) struct.count = len(self.tokens) struct.type = core.BNNewTypeReference(self.type.handle) + struct.parentType = core.BNNewTypeReference(self.parent_type.handle) struct.rootType = core.BNNewTypeReference(self.root_type.handle) struct.rootTypeName = self.root_type_name if self.base_type is None: diff --git a/suite/api_test.py b/suite/api_test.py index 7366adad..dfba75ae 100644 --- a/suite/api_test.py +++ b/suite/api_test.py @@ -1028,7 +1028,7 @@ class TestTypePrinter(unittest.TestCase): InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0), InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0), InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0) - ], type, type, '', 0, 1) + ], type, type, type, '', 0, 1), ] MyTypePrinter().register() @@ -467,6 +467,7 @@ TypeDefinitionLine TypeDefinitionLine::FromAPIObject(BNTypeDefinitionLine* line) result.lineType = line->lineType; result.tokens = InstructionTextToken::ConvertInstructionTextTokenList(line->tokens, line->count); result.type = new Type(BNNewTypeReference(line->type)); + result.parentType = new Type(BNNewTypeReference(line->parentType)); result.rootType = new Type(BNNewTypeReference(line->rootType)); result.rootTypeName = line->rootTypeName; result.baseType = line->baseType ? new NamedTypeReference(BNNewNamedTypeReference(line->baseType)) : nullptr; @@ -487,6 +488,7 @@ BNTypeDefinitionLine* TypeDefinitionLine::CreateTypeDefinitionLineList( result[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(lines[i].tokens); result[i].count = lines[i].tokens.size(); result[i].type = BNNewTypeReference(lines[i].type->GetObject()); + result[i].parentType = BNNewTypeReference(lines[i].parentType->GetObject()); result[i].rootType = BNNewTypeReference(lines[i].rootType->GetObject()); result[i].rootTypeName = BNAllocString(lines[i].rootTypeName.c_str()); result[i].baseType = lines[i].baseType ? BNNewNamedTypeReference(lines[i].baseType->GetObject()) : nullptr; @@ -504,6 +506,7 @@ void TypeDefinitionLine::FreeTypeDefinitionLineList(BNTypeDefinitionLine* lines, { InstructionTextToken::FreeInstructionTextTokenList(lines[i].tokens, lines[i].count); BNFreeType(lines[i].type); + BNFreeType(lines[i].parentType); BNFreeType(lines[i].rootType); BNFreeNamedTypeReference(lines[i].baseType); BNFreeString(lines[i].rootTypeName); @@ -1213,6 +1216,7 @@ std::vector<TypeDefinitionLine> Type::GetLines(const TypeContainer& types, const line.lineType = list[i].lineType; line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(list[i].tokens, list[i].count); line.type = new Type(BNNewTypeReference(list[i].type)); + line.parentType = list[i].parentType ? new Type(BNNewTypeReference(list[i].parentType)) : nullptr; line.rootType = list[i].rootType ? new Type(BNNewTypeReference(list[i].rootType)) : nullptr; line.rootTypeName = list[i].rootTypeName; line.baseType = list[i].baseType ? new NamedTypeReference(BNNewNamedTypeReference(list[i].baseType)) : nullptr; diff --git a/ui/action.h b/ui/action.h index 38ca296a..9deb3a66 100644 --- a/ui/action.h +++ b/ui/action.h @@ -33,6 +33,7 @@ struct LinearViewCursorPosition; struct BINARYNINJAUIAPI HighlightTokenState { bool valid; + bool focused; bool secondaryHighlight; BNInstructionTextTokenType type; BinaryNinja::InstructionTextToken token; diff --git a/ui/render.h b/ui/render.h index 02b0a66c..8dc0d158 100644 --- a/ui/render.h +++ b/ui/render.h @@ -69,7 +69,9 @@ class BINARYNINJAUIAPI FontParameters FontParameters(QWidget* parent, float fontScale = 1.0f); void update(); + const QFont& getFont() const { return m_font; } QFont& getFont() { return m_font; } + const QFont& getEmojiFont() const { return m_emojiFont; } QFont& getEmojiFont() { return m_emojiFont; } void setFont(const QFont& font); void setEmojiFont(const QFont& emojiFont); @@ -94,6 +96,7 @@ class BINARYNINJAUIAPI RenderContext void update(); FontParameters& getFontParamters() { return m_fontParams; } + const FontParameters& getFontParameters() const { return m_fontParams; } int getFontWidth() const { return m_fontParams.getWidth(); } int getFontHeight() const { return m_fontParams.getHeight(); } @@ -113,7 +116,7 @@ class BINARYNINJAUIAPI RenderContext void drawUnderlinedText(QPainter& p, int x, int y, QColor color, const QString& text); void drawSeparatorLine(QPainter& p, QColor top, QColor bottom, QColor line, const QRect& rect); - void drawInstructionHighlight(QPainter& p, const QRect& rect); + void drawInstructionHighlight(QPainter& p, const QRect& rect, bool focused = true); void drawLinearDisassemblyLineBackground( QPainter& p, BNLinearDisassemblyLineType type, const QRect& rect, const QRect& dirtyRect, int gutterWidth); @@ -50,6 +50,7 @@ void BINARYNINJAUIAPI addJsonTheme(const char* json); QColor BINARYNINJAUIAPI getThemeColor(BNThemeColor color); QColor BINARYNINJAUIAPI getTokenColor(QWidget* widget, BNInstructionTextTokenType token); +QColor BINARYNINJAUIAPI getTypeClassColor(const QWidget* widget, BNTypeClass typeClass); QColor BINARYNINJAUIAPI avgColor(QColor a, QColor b); QColor BINARYNINJAUIAPI mixColor(QColor a, QColor b, uint8_t mix); diff --git a/ui/tokenizedtextwidget.h b/ui/tokenizedtextwidget.h new file mode 100644 index 00000000..acbb2af5 --- /dev/null +++ b/ui/tokenizedtextwidget.h @@ -0,0 +1,254 @@ +#pragma once + +#include <QtWidgets/QAbstractScrollArea> +#include <QtCore/QTimer> +#include <optional> +#include "binaryninjaapi.h" +#include "viewframe.h" +#include "render.h" +#include "commentdialog.h" +#include "menus.h" +#include "uicontext.h" + +/*! + + \defgroup tokenizedtextwidget TokenizedTextWidget + \ingroup uiapi +*/ + + +/*! + + \ingroup tokenizedtextwidget +*/ +enum BINARYNINJAUIAPI TokenizedTextWidgetSelectionStyle +{ + NoSelection = 1 << 0, + SelectLines = 1 << 1, + SelectOneToken = 1 << 2, + SelectTokens = 1 << 3, + SelectCharacters = 1 << 4, + + AllStyles = NoSelection | SelectLines | SelectOneToken | SelectTokens | SelectCharacters, +}; + + +/*! + + \ingroup tokenizedtextwidget +*/ +struct BINARYNINJAUIAPI TokenizedTextWidgetCursorPosition +{ + /// Index of line in widget + size_t lineIndex = BN_INVALID_OPERAND; + /// Index of token in current line + size_t tokenIndex = BN_INVALID_OPERAND; + /// Index of character in current token + size_t characterIndex = BN_INVALID_OPERAND; + + // Directly from QMouseEvent, not used in comparator + int cursorX; + int cursorY; + + bool isValid() const { return lineIndex != BN_INVALID_OPERAND; } + bool operator==(const TokenizedTextWidgetCursorPosition& other) const; + bool operator!=(const TokenizedTextWidgetCursorPosition& other) const { return !(*this == other); } + bool operator<(const TokenizedTextWidgetCursorPosition& other) const; +}; + +/*! + QWidget that displays lines of InstructionTextTokens with the ability to make selections + + \ingroup tokenizedtextwidget +*/ +class BINARYNINJAUIAPI TokenizedTextWidget : + public QAbstractScrollArea +{ +protected: + struct LineMetadata + { + size_t charWidth; + int copyStyles; + + LineMetadata(): + charWidth(0), + copyStyles(TokenizedTextWidgetSelectionStyle::AllStyles) + {} + }; + struct TokenMetadata + { + size_t charOffset; + int copyStyles; + bool selectLineTarget; + + TokenMetadata(): + charOffset(0), + copyStyles(TokenizedTextWidgetSelectionStyle::AllStyles), + selectLineTarget(false) + {} + }; + +private: + Q_OBJECT + + UIActionHandler m_actionHandler; + ContextMenuManager* m_contextMenuManager; + Menu m_contextMenu; + + RenderContext m_render; + int m_cols, m_rows; + int m_contentsCols, m_contentsRows; + int m_verticalWheelDelta, m_horizontalWheelDelta; + bool m_updatingScrollBar; + bool m_autoScrollHorizontal, m_autoScrollVertical; + + TokenizedTextWidgetCursorPosition m_cursorPos, m_selectionStartPos, m_hoverPos; + TokenizedTextWidgetSelectionStyle m_selectionMode; + size_t m_hoverLine; + bool m_selectionStartedPastWidth; + bool m_selectionStartedPastHeight; + bool m_cursorKeys; + bool m_forceLineSelect; + + std::vector<BinaryNinja::LinearDisassemblyLine> m_lines; + std::vector<LineMetadata> m_lineMetadata; + std::vector<std::vector<TokenMetadata>> m_tokenMetadata; + DisassemblySettingsRef m_settings; + + void adjustSize(int width, int height); + void clampCursorPosition(TokenizedTextWidgetCursorPosition& pos); + void clampSelectionToValid(); + + protected: + virtual void updateMetadata(const std::vector<BinaryNinja::LinearDisassemblyLine>& lines, int& width, int& height); + + private Q_SLOTS: + void verticalScrollBarMoved(int value); + void verticalScrollBarAction(int action); + void horizontalScrollBarMoved(int value); + void horizontalScrollBarAction(int action); + + public: + explicit TokenizedTextWidget(QWidget* parent, + const std::vector<BinaryNinja::LinearDisassemblyLine>& lines = + std::vector<BinaryNinja::LinearDisassemblyLine>()); + virtual ~TokenizedTextWidget(); + + void bindActions(); + + QFont font() const; + void setFont(const QFont& font); + + DisassemblySettingsRef settings() { return m_settings; } + const DisassemblySettingsRef& settings() const { return m_settings; } + + int topLineIndex() const; + int leftmostCharIndex() const; + int visibleColumnCount() const { return m_cols; } + int visibleRowCount() const { return m_rows; } + int contentsColumnCount() const { return m_contentsCols; } + int contentsRowCount() const { return m_contentsRows; } + + bool hasSelection() const; + // Lines vs Tokens vs Characters vs NoSelection + TokenizedTextWidgetSelectionStyle selectionStyle() const; + // Lower bound of selection + TokenizedTextWidgetCursorPosition selectionBegin() const; + // Upper bound of selection + TokenizedTextWidgetCursorPosition selectionEnd() const; + // Originally highlighted selection base + TokenizedTextWidgetCursorPosition selectionBase() const; + // Position of cursor for movement operations + TokenizedTextWidgetCursorPosition cursorPosition() const; + bool forceLineSelect() const { return m_forceLineSelect; } + void setForceLineSelect(bool value) { m_forceLineSelect = value; } + + void setSelection(TokenizedTextWidgetCursorPosition base, TokenizedTextWidgetCursorPosition cursor, TokenizedTextWidgetSelectionStyle mode); + void setCursorPosition(TokenizedTextWidgetCursorPosition newPosition, bool selecting, bool cursorKeys, bool evenIfNoChange); + void moveCursorHorizontal(int count, bool allTheWay, bool selecting, bool cursorKeys); + void moveCursorVertical(int count, bool allTheWay, bool selecting, bool cursorKeys); + + bool autoScrollHorizontal() const { return m_autoScrollHorizontal; } + void setAutoScrollHorizontal(bool value) { m_autoScrollHorizontal = value; } + bool autoScrollVertical() const { return m_autoScrollVertical; } + void setAutoScrollVertical(bool value) { m_autoScrollVertical = value; } + + HighlightTokenState highlightTokenState(); + UIActionHandler* actionHandler() { return &m_actionHandler; } + + virtual UIActionContext actionContext(); + Menu& contextMenu() { return m_contextMenu; } + void showContextMenu(); + + void left(size_t count, bool selecting); + void right(size_t count, bool selecting); + void leftToWord(bool selecting); + void rightToWord(bool selecting); + void up(bool selecting); + void down(bool selecting); + void pageUp(bool selecting); + void pageDown(bool selecting); + void moveToStartOfLine(bool selecting); + void moveToEndOfLine(bool selecting); + void moveToStartOfView(bool selecting); + void moveToEndOfView(bool selecting); + void selectAll(); + void selectNone(); + + void scrollLines(int count); + void scrollLineToVisible(int lineIndex); + void scrollLineToTop(int lineIndex); + + void scrollChars(int count); + void scrollCharToVisible(int charIndex); + void scrollCharToLeftmost(int charIndex); + + void copy() const; + std::string selectedText() const; + + const std::vector<BinaryNinja::LinearDisassemblyLine>& lines() const { return m_lines; } + std::optional<std::reference_wrapper<const BinaryNinja::LinearDisassemblyLine>> lineAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<std::reference_wrapper<const BinaryNinja::InstructionTextToken>> tokenAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<char> charAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + + void clearLines(); + void setLines(const std::vector<BinaryNinja::LinearDisassemblyLine>& lines, bool resetScroll = true); + void setLines(const std::vector<BinaryNinja::DisassemblyTextLine>& lines, bool resetScroll = true); + void setLines(const std::vector<BinaryNinja::TypeDefinitionLine>& lines, bool resetScroll = true); + + int lineCopyStyles(size_t lineIndex) const; + void setLineCopyStyles(size_t lineIndex, int styles); + + int tokenCopyStyles(size_t lineIndex, size_t tokenIndex) const; + void setTokenCopyStyles(size_t lineIndex, size_t tokenIndex, int styles); + bool tokenSelectLineTarget(size_t lineIndex, size_t tokenIndex) const; + void setTokenSelectLineTarget(size_t lineIndex, size_t tokenIndex, bool selectLineTarget); + + Q_SIGNALS: + void sizeChanged(int cols, int rows); + void visibleChanged(int leftCol, int topRow); + void linesChanged(); + void selectionChanged(const TokenizedTextWidgetCursorPosition& begin, const TokenizedTextWidgetCursorPosition& end); + void tokenLeftClicked(const TokenizedTextWidgetCursorPosition& position); + void tokenRightClicked(const TokenizedTextWidgetCursorPosition& position); + void tokenDoubleClicked(const TokenizedTextWidgetCursorPosition& position); + void tokenOtherClicked(const TokenizedTextWidgetCursorPosition& position, Qt::MouseButton button); + void tokenHovered(const TokenizedTextWidgetCursorPosition& position); + void lineLeftClicked(size_t lineIndex); + void lineRightClicked(size_t lineIndex); + void lineDoubleClicked(size_t lineIndex); + void lineOtherClicked(size_t lineIndex, Qt::MouseButton button); + void lineHovered(size_t lineIndex); + + protected: + virtual void resizeEvent(QResizeEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + virtual void wheelEvent(QWheelEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + virtual void leaveEvent(QEvent* event) override; + virtual void focusInEvent(QFocusEvent* event) override; + virtual void focusOutEvent(QFocusEvent* event) override; + virtual void contextMenuEvent(QContextMenuEvent* event) override; +}; diff --git a/ui/typebrowser.h b/ui/typebrowser.h new file mode 100644 index 00000000..d74fe341 --- /dev/null +++ b/ui/typebrowser.h @@ -0,0 +1,524 @@ +#pragma once + +#include <QtWidgets/QTreeView> +#include <QtCore/QSortFilterProxyModel> +#include <QtGui/QStandardItemModel> +#include <QtWidgets/QItemDelegate> +#include <QtWidgets/QTextEdit> +#include <memory> +#include "sidebar.h" +#include "viewframe.h" +#include "filter.h" +#include "progresstask.h" +#include "typeeditor.h" + + +enum BINARYNINJAUIAPI TypeBrowserFilterMode +{ + NamesOnly = 0, + NamesAndMembers = 1, + FullDefinitions = 2 +}; + + +class BINARYNINJAUIAPI TypeBrowserTreeNode : public std::enable_shared_from_this<TypeBrowserTreeNode> +{ +public: + struct UpdateData + { + enum UpdateType + { + NodeInserted, + NodeUpdated, + NodeRemoved, + UpdatesFinished, + }; + + UpdateType type; + std::shared_ptr<TypeBrowserTreeNode> parent; + std::shared_ptr<TypeBrowserTreeNode> node; + std::function<void(const UpdateData&)> commit; + }; + + typedef std::function<void(UpdateData)> UpdateNodeCallback; + +protected: + class TypeBrowserModel* m_model; + std::optional<std::weak_ptr<TypeBrowserTreeNode>> m_parent; + std::vector<std::shared_ptr<TypeBrowserTreeNode>> m_children; + std::map<const TypeBrowserTreeNode*, size_t> m_childIndices; + bool m_hasGeneratedChildren; + + TypeBrowserTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent); + virtual ~TypeBrowserTreeNode() = default; + virtual void generateChildren() = 0; + void updateChildIndices(); + + void removeChild(std::shared_ptr<TypeBrowserTreeNode> child); + void addChild(std::shared_ptr<TypeBrowserTreeNode> child); + +public: + class TypeBrowserModel* model() const { return m_model; } + std::optional<std::shared_ptr<TypeBrowserTreeNode>> parent() const; + const std::vector<std::shared_ptr<TypeBrowserTreeNode>>& children(); + int indexOfChild(std::shared_ptr<const TypeBrowserTreeNode> child) const; + + virtual std::string text(int column) const = 0; + virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const = 0; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const = 0; + virtual void updateChildren(bool recursive, UpdateNodeCallback update); +}; + + +class BINARYNINJAUIAPI EmptyTreeNode : public TypeBrowserTreeNode +{ +public: + EmptyTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent); + virtual ~EmptyTreeNode() = default; + + virtual std::string text(int column) const override; + virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + +protected: + virtual void generateChildren() override; + virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; +}; + + +class BINARYNINJAUIAPI RootTreeNode : public TypeBrowserTreeNode +{ + std::map<std::string, std::shared_ptr<class TypeContainerTreeNode>> m_containerNodes; + +public: + RootTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent); + virtual ~RootTreeNode() = default; + + virtual std::string text(int column) const override; + virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + +protected: + virtual void generateChildren() override; + virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; +}; + + +class BINARYNINJAUIAPI TypeTreeNode : public TypeBrowserTreeNode +{ +public: + enum SourceType + { + None, + TypeLibrary, + DebugInfo, + Platform, + Other + }; + +private: + std::string m_id; + BinaryNinja::QualifiedName m_name; + TypeRef m_type; + std::string m_sortName; + + SourceType m_sourceType; + std::optional<TypeLibraryRef> m_sourceLibrary; + std::optional<std::string> m_sourceDebugInfoParser; + std::optional<PlatformRef> m_sourcePlatform; + std::optional<std::string> m_sourceOtherName; + std::optional<BinaryNinja::QualifiedName> m_sourceOriginalName; + +public: + TypeTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent, const std::string& id, BinaryNinja::QualifiedName name, TypeRef type); + virtual ~TypeTreeNode() = default; + + const std::string& id() const { return m_id; } + const BinaryNinja::QualifiedName& name() const { return m_name; } + const TypeRef& type() const { return m_type; } + void setType(const std::string& id, const BinaryNinja::QualifiedName& name, const TypeRef& type); + + const SourceType& sourceType() const { return m_sourceType; } + std::optional<BinaryNinja::TypeContainer> typeContainer() const; + std::optional<BinaryNinja::TypeContainer> sourceTypeContainer() const; + PlatformRef sourcePlatform() const; + + virtual std::string text(int column) const override; + virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + +protected: + virtual void generateChildren() override; +}; + + +class BINARYNINJAUIAPI TypeContainerTreeNode : public TypeBrowserTreeNode +{ + std::string m_containerId; + // TODO: Gross + std::map<std::string, std::pair<std::pair<BinaryNinja::QualifiedName, TypeRef>, std::shared_ptr<TypeTreeNode>>> m_typeNodes; + +public: + TypeContainerTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent, const std::string& m_containerId); + virtual ~TypeContainerTreeNode(); + + virtual std::string text(int column) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; + + const std::string& containerId() const { return m_containerId; } + std::optional<PlatformRef> platform() const; + std::optional<BinaryNinja::TypeContainer> typeContainer() const; + std::optional<BNTypeContainerType> containerType() const; + virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; + +protected: + virtual void generateChildren() override; +}; + +//----------------------------------------------------------------------------- + + +class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public BinaryNinja::BinaryDataNotification +{ + Q_OBJECT + BinaryViewRef m_data; + std::shared_ptr<TypeBrowserTreeNode> m_rootNode; + mutable std::recursive_mutex m_rootNodeMutex; + bool m_needsUpdate; + bool m_updating; + + std::recursive_mutex m_updateMutex; + std::vector<std::function<void()>> m_updateCallbacks; + + std::vector<std::string> m_containerIds; + std::map<std::string, std::string> m_containerNames; + std::map<std::string, BNTypeContainerType> m_containerTypes; + std::map<std::string, BinaryNinja::TypeContainer> m_containers; + + std::map<std::string, BinaryViewRef> m_containerViews; + std::map<std::string, TypeLibraryRef> m_containerLibraries; + std::map<std::string, DebugInfoRef> m_containerDebugInfos; + std::map<std::string, PlatformRef> m_containerPlatforms; + + void updateContainerList(); + void callUpdateCallbacks(); + void commitUpdates(std::vector<TypeBrowserTreeNode::UpdateData>& updates); + +public: + TypeBrowserModel(BinaryViewRef data); + virtual ~TypeBrowserModel(); + BinaryViewRef getData() { return m_data; } + std::shared_ptr<TypeBrowserTreeNode> getRootNode() { return m_rootNode; } + + std::vector<std::string> containerIds() const; + + std::string nameForContainerId(const std::string& id) const; + std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id); + std::optional<std::reference_wrapper<const BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id) const; + std::optional<BinaryViewRef> viewForContainerId(const std::string& id) const; + std::optional<TypeLibraryRef> libraryForContainerId(const std::string& id) const; + std::optional<DebugInfoRef> debugInfoForContainerId(const std::string& id) const; + std::optional<PlatformRef> platformForContainerId(const std::string& id) const; + + void updateFonts(); + void runAfterUpdate(std::function<void()> callback); + + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QModelIndex parent(const QModelIndex& child) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + std::shared_ptr<TypeBrowserTreeNode> nodeForIndex(const QModelIndex& index) const; + QModelIndex indexForNode(std::shared_ptr<TypeBrowserTreeNode> node, int column = 0) const; + + std::vector<std::shared_ptr<TypeContainerTreeNode>> containerNodes() const; + + bool filter(const QModelIndex& index, const std::string& filter, TypeBrowserFilterMode mode) const; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const; + + void OnTypeDefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; + void OnTypeUndefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; + void OnTypeReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; + void OnTypeFieldReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, uint64_t offset) override; + +Q_SIGNALS: + void updatesAboutToHappen(); + void updateComplete(); + +public Q_SLOTS: + void markDirty(); + void notifyRefresh(); +}; + + +class BINARYNINJAUIAPI TypeBrowserFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + BinaryViewRef m_data; + TypeBrowserModel* m_model; + std::string m_filter; + TypeBrowserFilterMode m_filterMode; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; + bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; + +public: + TypeBrowserFilterModel(BinaryViewRef data, TypeBrowserModel* model); + + void setFilter(const std::string& filter); + TypeBrowserFilterMode filterMode() const { return m_filterMode; } + void setFilterMode(TypeBrowserFilterMode newMode) { m_filterMode = newMode; } + +Q_SIGNALS: + void filterAboutToBeChanged(); + void filterChanged(); +}; + + +class BINARYNINJAUIAPI TypeBrowserItemDelegate : public QItemDelegate +{ + QFont m_font; + QFont m_monospaceFont; + float m_charWidth, m_charHeight, m_charOffset; + float m_baseline; + class TypeBrowserView* m_view; + + void initFont(); +public: + TypeBrowserItemDelegate(class TypeBrowserView* view); + void updateFonts(); + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; +}; + + +class BINARYNINJAUIAPI TypeBrowserTreeView : public QTreeView +{ + Q_OBJECT + UIActionHandler m_actionHandler; + +public: + explicit TypeBrowserTreeView(class TypeBrowserView* parent); +}; + + +struct BINARYNINJAUIAPI TypeReference +{ + std::string containerId; + BinaryNinja::QualifiedName typeName; + + TypeReference() = default; + TypeReference(std::string containerId, BinaryNinja::QualifiedName typeName); +}; + + +class BINARYNINJAUIAPI TypeBrowserView : public QFrame, public View, public FilterTarget +{ + Q_OBJECT + ViewFrame* m_frame; + BinaryViewRef m_data; + class TypeBrowserContainer* m_container; + ContextMenuManager* m_contextMenuManager; + + QSplitter* m_splitter; + + TypeBrowserModel* m_model; + TypeBrowserFilterModel* m_filterModel; + QStandardItemModel* m_loadingModel; + QTreeView* m_tree; + TypeBrowserItemDelegate* m_delegate; + bool m_updatedWidths; + + bool m_navigateToNextInsert; + QModelIndex m_lastPosition; + QModelIndex m_lastInsert; + TypeEditor::SavedCursorPosition m_editorPosition; + + TypeEditor* m_typeEditor; + QTextEdit* m_debugText; + +public: + TypeBrowserView(ViewFrame* frame, BinaryViewRef data, TypeBrowserContainer* container); + + TypeBrowserContainer* getContainer() { return m_container; } + TypeBrowserModel* getModel() { return m_model; } + TypeBrowserFilterModel* getFilterModel() { return m_filterModel; } + QTreeView* getTreeView() { return m_tree; } + TypeEditor* getTypeEditor() { return m_typeEditor; } + + virtual BinaryViewRef getData() override { return m_data; } + virtual uint64_t getCurrentOffset() override; + virtual void setSelectionOffsets(BNAddressRange range) override; + virtual bool navigate(uint64_t offset) override; + virtual SelectionInfoForXref getSelectionForXref() override; + virtual QFont getFont() override; + virtual void updateFonts() override; + + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; + + virtual StatusBarWidget* getStatusBarWidget() override; + virtual QWidget* getHeaderOptionsWidget() override; + + virtual void setFilter(const std::string& filter) override; + virtual void scrollToFirstItem() override; + virtual void scrollToCurrentItem() override; + virtual void selectFirstItem() override; + virtual void activateFirstItem() override; + + virtual void notifyRefresh() override; + + void showSelectedTypes(); + void showTypes(const std::vector<TypeReference>& types); + void selectTypeByName(const std::string& name, bool newSelection); + + bool navigateToType(const std::string& typeName, uint64_t offset); + void scrollToIndexWithContext(const QModelIndex& index, int context = 1); + + // Selection helpers + + // All nodes + std::vector<std::shared_ptr<TypeBrowserTreeNode>> selectedNodes() const; + // BV selected or BV relevant to selected types, only if JUST bv stuff is selected + std::optional<BinaryViewRef> selectedBV() const; + // If selectedBV exists, names of selected types + std::optional<std::unordered_set<BinaryNinja::QualifiedName>> selectedBVTypeNames() const; + + std::optional<std::pair<BinaryNinja::TypeContainer, BinaryNinja::QualifiedName>> selectedTypeNameAndContainer() const; + // All selected type names, grouped by type container + std::vector<std::pair<BinaryNinja::TypeContainer, std::vector<BinaryNinja::QualifiedName>>> selectedTypeNamesByContainers() const; + // Selected type reference + std::optional<TypeReference> selectedType() const; + // Selected type references + std::vector<TypeReference> selectedTypes() const; + // Selected type container, or container of selected type + // makeSureItHasPlatform: if the type container is a BV with no platform (raw), ask for one and return nullopt if rejected + // preferView: if the type container is a BV and the user/auto-only container, switch to the whole-view container for that BV instead + std::optional<BinaryNinja::TypeContainer> selectedTypeContainer(bool makeSureItHasPlatform = true, bool preferView = false) const; + + // Names -> Ids, if any don't exist then nullopt + static std::optional<std::unordered_set<std::string>> typeIdsFromNames(BinaryViewRef view, const std::unordered_set<BinaryNinja::QualifiedName>& names); + + std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForId(const std::string& containerId, bool makeSureItHasPlatform = false, bool preferView = false); + + // Menu actions + static void registerActions(); + void bindActions(); + void showContextMenu(); + + bool canCreateNewTypes(); + void createNewTypes(); + bool canCreateNewStructure(); + void createNewStructure(); + bool canCreateNewEnumeration(); + void createNewEnumeration(); + bool canCreateNewUnion(); + void createNewUnion(); + bool canRenameTypes(); + void renameTypes(); + bool canDeleteTypes(); + void deleteTypes(); + bool canChangeTypes(); + void changeTypes(); + bool canImportType(); + void importType(); + bool canAddTypeLibrary(); + void addTypeLibrary(); + bool canExpandAll(); + void expandAll(); + bool canCollapseAll(); + void collapseAll(); + +Q_SIGNALS: + void typeNameNavigated(const std::string& typeName, bool newSelection); + +protected: + void itemSelected(const QItemSelection& selected, const QItemSelection& deselected); + void itemDoubleClicked(const QModelIndex& index); + virtual void contextMenuEvent(QContextMenuEvent* event) override; +}; + +class BINARYNINJAUIAPI TypeBrowserOptionsIconWidget : public QWidget +{ +public: + TypeBrowserOptionsIconWidget(TypeBrowserView* parent); + +private: + TypeBrowserView* m_view; + + void showMenu(); +}; + +class BINARYNINJAUIAPI TypeBrowserContainer : public QWidget, public ViewContainer +{ + Q_OBJECT + + ViewFrame* m_frame; + BinaryViewRef m_data; + TypeBrowserView* m_view; + FilteredView* m_filter; + FilterEdit* m_separateEdit; + class TypeBrowserSidebarWidget* m_sidebarWidget; + UIActionHandler m_actionHandler; + +public: + TypeBrowserContainer(ViewFrame* frame, BinaryViewRef data, class TypeBrowserSidebarWidget* parent); + virtual View* getView() override { return m_view; } + + ViewFrame* getViewFrame() { return m_frame; } + BinaryViewRef getData() { return m_data; } + TypeBrowserView* getTypeBrowserView() { return m_view; } + FilteredView* getFilter() { return m_filter; } + FilterEdit* getSeparateFilterEdit() { return m_separateEdit; } + class TypeBrowserSidebarWidget* getSidebarWidget() { return m_sidebarWidget; } + void showContextMenu(); + +protected: + virtual void focusInEvent(QFocusEvent* event) override; +}; + + +class BINARYNINJAUIAPI TypeBrowserViewType : public ViewType +{ + static TypeBrowserViewType* g_instance; + +public: + TypeBrowserViewType(); + virtual int getPriority(BinaryViewRef data, const QString& filename) override; + virtual QWidget* create(BinaryViewRef data, ViewFrame* frame) override; + static void init(); +}; + + +class BINARYNINJAUIAPI TypeBrowserSidebarWidget : public SidebarWidget +{ + Q_OBJECT + + QWidget* m_header; + TypeBrowserContainer* m_container; + +public: + TypeBrowserSidebarWidget(ViewFrame* frame, BinaryViewRef data); + TypeBrowserContainer* container() { return m_container; } + virtual QWidget* headerWidget() override { return m_header; } + virtual void focus() override; + +protected: + virtual void contextMenuEvent(QContextMenuEvent* event) override; + +private Q_SLOTS: + void showContextMenu(); +}; + + +class BINARYNINJAUIAPI TypeBrowserSidebarWidgetType : public SidebarWidgetType +{ +public: + TypeBrowserSidebarWidgetType(); + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; diff --git a/ui/typeeditor.h b/ui/typeeditor.h new file mode 100644 index 00000000..4de99778 --- /dev/null +++ b/ui/typeeditor.h @@ -0,0 +1,168 @@ + +#pragma once + +#include "tokenizedtextwidget.h" +#include "uitypes.h" + +class BINARYNINJAUIAPI TypeEditor: public TokenizedTextWidget +{ +public: + struct SavedCursorPosition + { + struct PositionData + { + BinaryNinja::QualifiedName typeName; + TokenizedTextWidgetCursorPosition position; + size_t structOffset; + std::pair<size_t, size_t> lineStart; + }; + bool restoreCursor; + bool restoreTop; + TokenizedTextWidgetSelectionStyle style; + PositionData cursor; + PositionData base; + PositionData top; + }; + +private: + Q_OBJECT + + PlatformRef m_platform; + std::optional<BinaryNinja::TypeContainer> m_typeContainer; + std::optional<BinaryViewRef> m_binaryView; + // Empty view for bv-requiring operations + mutable std::optional<BinaryViewRef> m_emptyView; + std::vector<BinaryNinja::QualifiedName> m_typeNames; + + // line index -> index of first line from wrapped line + std::vector<size_t> m_lineUnwrapIndex; + // line index -> type name + std::vector<BinaryNinja::QualifiedName> m_lineTypeRefs; + // type name -> index of first line + std::map<BinaryNinja::QualifiedName, size_t> m_lineTypeStarts; + // type name -> { offset -> index of first { line, token } at offset } + std::map<BinaryNinja::QualifiedName, std::map<size_t, std::pair<size_t, size_t>>> m_lineTypeOffsetStarts; + // type name -> { offset -> index of last { line, token } at offset } + std::map<BinaryNinja::QualifiedName, std::map<size_t, std::pair<size_t, size_t>>> m_lineTypeOffsetEnds; + // line index -> line + std::vector<BinaryNinja::TypeDefinitionLine> m_typeLines; + + TokenizedTextWidgetCursorPosition m_originalBase; + + bool m_wrapLines; + bool m_showInherited; + +public: + TypeEditor(QWidget* parent); + + static void registerActions(); + void bindActions(); + + PlatformRef platform() const { return m_platform; } + void setPlatform(PlatformRef platform) { m_platform = platform; } + + std::optional<BinaryViewRef> binaryView() const { return m_binaryView; } + void setBinaryView(std::optional<BinaryViewRef> binaryView) { m_binaryView = binaryView; } + + std::optional<std::reference_wrapper<const BinaryNinja::TypeContainer>> typeContainer() const; + void setTypeContainer(std::optional<BinaryNinja::TypeContainer> container); + + std::vector<BinaryNinja::QualifiedName> typeNames() const { return m_typeNames; } + void setTypeNames(const std::vector<BinaryNinja::QualifiedName>& names); + + int selectedLineCount() const; + std::unordered_set<size_t> selectedLineStarts() const; + int selectedRootTypeCount() const; + std::unordered_set<BinaryNinja::QualifiedName> selectedRootTypes() const; + std::optional<size_t> firstWrappedLineIndexForLineIndex(size_t lineIndex) const; + std::optional<size_t> lastWrappedLineIndexForLineIndex(size_t lineIndex) const; + std::optional<std::reference_wrapper<const BinaryNinja::TypeDefinitionLine>> typeLineAtIndex(size_t lineIndex) const; + std::optional<std::reference_wrapper<const BinaryNinja::TypeDefinitionLine>> typeLineAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<BinaryNinja::QualifiedName> rootTypeNameAtIndex(size_t lineIndex) const; + std::optional<BinaryNinja::QualifiedName> rootTypeNameAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<TypeRef> rootTypeAtIndex(size_t lineIndex) const; + std::optional<TypeRef> rootTypeAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<uint64_t> offsetAtIndex(size_t lineIndex) const; + std::optional<uint64_t> offsetAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<int64_t> relativeOffsetAtPosition(const TokenizedTextWidgetCursorPosition& position) const; + std::optional<TokenizedTextWidgetCursorPosition> firstPositionForOffset(const BinaryNinja::QualifiedName& name, uint64_t offset) const; + std::optional<TokenizedTextWidgetCursorPosition> lastPositionForOffset(const BinaryNinja::QualifiedName& name, uint64_t offset) const; + void selectOffsetRange(const BinaryNinja::QualifiedName& name, uint64_t start, uint64_t end); + + SavedCursorPosition saveCursorPosition() const; + void restoreCursorPosition(const SavedCursorPosition& position); + + bool canCreateAllMembersForStructure(); + void createAllMembersForStructure(); + bool canCreateCurrentMemberForStructure(); + void createCurrentMemberForStructure(); + bool canRename(); + void rename(); + void renameRoot(); + void renameMember(); + bool canUndefine(); + void undefine(); + void undefineRoots(); + void undefineMembers(); + bool canCreateArray(); + void createArray(); + bool canChangeType(); + void changeType(); + void changeTypeRoot(); + void changeTypeAddMember(); + void changeTypeMember(); + bool canChangeTypeMembers(); + void changeTypeMembers(TypeRef newType); + bool canSetStructureSize(); + void setStructureSize(); + bool canAddUserXref(); + void addUserXref(); + bool canMakePointer(); + void makePointer(); + bool canMakeCString(); + void makeCString(); + bool canMakeUTF16String(); + void makeUTF16String(); + bool canMakeUTF32String(); + void makeUTF32String(); + bool canCycleIntegerSize(); + void cycleIntegerSize(); + bool canCycleFloatSize(); + void cycleFloatSize(); + bool canInvertIntegerSize(); + void invertIntegerSize(); + bool canMakeInt8(); + void makeInt8(); + bool canMakeInt16(); + void makeInt16(); + bool canMakeInt32(); + void makeInt32(); + bool canMakeInt64(); + void makeInt64(); + bool canMakeFloat32(); + void makeFloat32(); + bool canMakeFloat64(); + void makeFloat64(); + bool canGoToAddress(bool selecting); + void goToAddress(bool selecting); + void toggleWrapLines(); + void toggleShowInherited(); + + std::string getDebugText(); + +Q_SIGNALS: + void typeNameNavigated(const std::string& typeName); + void currentTypeUpdated(const BinaryNinja::QualifiedName& typeName); + void currentTypeDeleted(const BinaryNinja::QualifiedName& typeName); + void currentTypeNameUpdated(const BinaryNinja::QualifiedName& typeName); + +private: + void updateLines(); + BinaryViewRef binaryViewOrEmpty() const; + void updateInTransaction(std::function<void()> transaction); + void updateInTransaction(std::function<void(BinaryViewRef)> transaction); + std::string dumpType(TypeRef type); + + void forEachMember(const TokenizedTextWidgetCursorPosition& begin, const TokenizedTextWidgetCursorPosition& end, + std::function<void(TypeRef /* type */, TypeRef /* parent */, size_t /* memberIndex */, size_t /* rootOffset */)> func, bool childrenFirst = false); +}; @@ -31,6 +31,9 @@ void BINARYNINJAUIAPI showTextTooltip(QWidget* parent, const QPoint& previewPos, bool BINARYNINJAUIAPI isBinaryNinjaDataBase(QFileInfo& info, QFileAccessor& accessor); +PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, BinaryViewRef data); +PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, PlatformRef defaultValue); + /*! @} */ diff --git a/ui/viewframe.h b/ui/viewframe.h index 59e45a75..7f4376e8 100644 --- a/ui/viewframe.h +++ b/ui/viewframe.h @@ -136,6 +136,7 @@ class BINARYNINJAUIAPI View virtual ~View() {} void enableRefreshTimer(QWidget* owner, int interval); + void setRefreshTimerRunning(bool running); void setRefreshQuiesce(bool enable); virtual void notifyRefresh() {}; |
