diff options
| author | Jon Palmisciano <jp@jonpalmisc.com> | 2021-06-30 09:26:43 -0400 |
|---|---|---|
| committer | Jon Palmisciano <jp@jonpalmisc.com> | 2021-09-30 13:31:10 -0400 |
| commit | e908f63a0dd96927b9607a7d9f859281d183e33e (patch) | |
| tree | b3902a7b7f0458ca5510bd2873d74ac6b1cd9e77 /ui/stackview.h | |
| parent | 9c68215d3bedfd07faf6148351705b487826d834 (diff) | |
StackView: It's a view of the stack!
Diffstat (limited to 'ui/stackview.h')
| -rw-r--r-- | ui/stackview.h | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/ui/stackview.h b/ui/stackview.h new file mode 100644 index 00000000..311fa37d --- /dev/null +++ b/ui/stackview.h @@ -0,0 +1,266 @@ +#pragma once + +#include <QtWidgets/QAbstractScrollArea> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QDialog> +#include <QtWidgets/QLineEdit> + +#include "dockhandler.h" +#include "render.h" +#include "sidebar.h" +#include "uitypes.h" + +//! Dialog to enable arbitrary stack variable creation. +class BINARYNINJAUIAPI CreateStackVariableDialog : public QDialog +{ + Q_OBJECT + + BinaryViewRef m_data; + FunctionRef m_func; + + QLineEdit* m_offsetField; + QLineEdit* m_nameField; + QComboBox* m_typeDropdown; + + //! Automatically update/sanitize the offset and name fields after input. + void autoFillFields(); + + protected: + void accept() override; + + public: + CreateStackVariableDialog( + QWidget* parent, BinaryViewRef data, FunctionRef func, int64_t initialOffset = 0); +}; + +//! A single line in the stack view. +class StackViewLine +{ + public: + enum class Type + { + Variable, + Member, + OffsetRef, + Annotation, + Fill + }; + + //! Create a new line for a variable. + static StackViewLine variable( + int64_t offset, const BinaryNinja::VariableNameAndType& vnat, PlatformRef plat); + + //! Create a new line for a struct or array member. + static StackViewLine member( + int64_t offset, const BinaryNinja::VariableNameAndType& vnat, PlatformRef plat); + + //! Create a new line for a struct offset reference. + static StackViewLine offsetRef(int64_t base, uint64_t offset, size_t size); + + //! Create a new annotation line. + static StackViewLine annotation(int64_t offset, const std::string& text); + + //! Create a new fill line. + static StackViewLine fill(int64_t offset, size_t length); + + //! Get this line's type. + StackViewLine::Type type() const; + + //! Get the stack frame offset for this line. + int64_t offset() const; + + //! Get the number of bytes this line represents on the stack. + size_t width() const; + + //! Get the hierarchical level of this line. + size_t level() const; + + //! Set the hierarchical level for this line. + void setLevel(size_t level); + + //! Get the type of the data this line represents. + TypeRef dataType() const; + + //! Set the type of the data this line represents. Does not affect the underlying variable. + void setDataType(TypeRef vnat); + + //! Get the variable represented by this line. + BinaryNinja::Variable variable() const; + + //! Set the variable represented by this line. + void setVariable(const BinaryNinja::Variable& var); + + //! Set a width override for this line. + void setWidthOverride(size_t width); + + //! Is this line backed by data or is it ephermeral? + bool isDataBacked() const; + + //! Is the data represented by this line referenced in the current function? + bool isReferenced() const; + + //! Set whether the data represented by this line is referenced in the current function. + void setIsReferenced(bool isReferenced); + + //! Is the data represented by this line unused in the current function? + bool isUnused() const; + + //! Set whether the data represented by this line unused in the current function. + void setIsUnused(bool isUnused); + + //! Get the content (with no offset) for this line. + BinaryNinja::DisassemblyTextLine content() const; + + //! Get the content with the leading offset (or padding) for this line. + BinaryNinja::DisassemblyTextLine contentWithOffset(bool hide = false) const; + + //! Get the width of the total line content in characters. + size_t contentWidth(bool withOffset = true) const; + + //! Append a single token to this line's content. + void appendToken(const BinaryNinja::InstructionTextToken& token); + + //! Append multiple tokens to this line's content. + void appendTokens(const std::vector<BinaryNinja::InstructionTextToken>& tokens); + + //! Indent this line's content. + void indent(size_t levels = 1); + + private: + StackViewLine(StackViewLine::Type type, int64_t offset); + + StackViewLine::Type m_type; + int64_t m_offset; + size_t m_level; + + TypeRef m_dataType; + BinaryNinja::Variable m_var; + size_t m_widthOverride; + + bool m_isReferenced; + bool m_isUnused; + + BinaryNinja::DisassemblyTextLine m_content; +}; + +//! Simple direction enum; used for cursor movement functions. +enum class Direction +{ + Up, + Down, + Left, + Right +}; + +//! The stack view! +class BINARYNINJAUIAPI StackView : public QAbstractScrollArea, public View +{ + Q_OBJECT + + ViewFrame* m_view; + BinaryViewRef m_data; + FunctionRef m_func; + RenderContext m_renderer; + + std::vector<StackViewLine> m_lines; + HighlightTokenState m_highlight; + size_t m_lineIndex; + size_t m_tokenIndex; + + //! Bind and register all stack view actions. + void setupActions(); + + //! Internal refresh method, rebuilds all lines. + void rebuildLines(); + + //! Refresh the internal highlight token state. + //! + //! \param shouldUpdateStatus Should UIContext::updateStatus() be called? + void refreshHighlight(bool shouldUpdateStatus = true); + + //! Find the end of a stack void given a start offset. + int64_t findVoidEnd(int64_t start) const; + + protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + + public: + StackView(ViewFrame* view, BinaryViewRef data); + + //! Refresh the stack view's content. + void refresh(); + + //! Move the cursor to approximate clicked position. + void moveCursorToMouse(QMouseEvent* event, bool isSelecting); + + //! Ensure the cursor is visible by adjusting the scroll position. + void ensureCursorIsVisible(); + + //! Move the cursor via the keyboard. + void moveCursor(Direction dir); + + //! Ensure the cursor is not selecting any prohibited tokens. + void sanitizeCursor(Direction preference); + + //! Get the selected StackViewLine. + const StackViewLine* selectedLine() const; + + //! Rename the variable belonging to the selected line. + void renameVariable(); + + //! Change the type of the variable belonging to the selected line. + void retypeVariable(); + + //! Undefine the variable belonging to the selected line. + void undefineVariable(); + + //! Show the "Create Variable" dialog. + void showCreateVariableDialog(); + + //! Create an integer of the given size at the cursor position. + void quickCreateIntegerAtCursor(size_t size); + + //! Create an array at the cursor, spanning until the next stack variable. + void quickCreateArrayAtCursor(); + + //! Create a new struct at the cursor, spanning until the next stack variable. + void quickCreateStructAtCursor(); + + //! Override the default event handler so we can have nice tooltips. + bool event(QEvent* event) override; + + BinaryViewRef getData() override; + uint64_t getCurrentOffset() override; + bool canCopyAddress() override { return false; }; + bool canPaste() override { return false; }; + void setSelectionOffsets(BNAddressRange range) override; + bool navigate(uint64_t offset) override; + QFont getFont() override; + void updateFonts() override; +}; + +//! Stack view sidebar widget wrapper. +class BINARYNINJAUIAPI StackViewSidebarWidget : public SidebarWidget +{ + Q_OBJECT + + StackView* m_stackView; + QWidget* m_header; + + public: + StackViewSidebarWidget(ViewFrame* view, BinaryViewRef data); + + void refresh(); + void focus() override { refresh(); } + void notifyFontChanged() override { m_stackView->updateFonts(); } + QWidget* headerWidget() override { return m_header; } +}; + +class BINARYNINJAUIAPI StackViewSidebarWidgetType : public SidebarWidgetType +{ + public: + StackViewSidebarWidgetType(); + SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; |
