diff --git a/ui/flowgraphwidget.h b/ui/flowgraphwidget.h index 29614c20aa..2f393fe092 100644 --- a/ui/flowgraphwidget.h +++ b/ui/flowgraphwidget.h @@ -10,6 +10,7 @@ #include "commentdialog.h" #include "commands.h" #include "instructionedit.h" +#include "tokennavigation.h" /*! @@ -72,6 +73,7 @@ class BINARYNINJAUIAPI FlowGraphWidget : public QAbstractScrollArea, public View, public PreviewScrollHandler, + public TokenNavigationHandler, public BinaryNinja::BinaryDataNotification { Q_OBJECT @@ -204,6 +206,16 @@ class BINARYNINJAUIAPI FlowGraphWidget : void navigateToAddress(uint64_t addr); void navigateToGotoLabel(uint64_t label); + BinaryViewRef getBinaryViewForTokenActivation() override; + QWidget* getWidgetForTokenActivation() override; + View* getViewForTokenActivation() override; + FunctionRef getFunctionForTokenActivation() override; + std::optional> getTokensForLineWithSelection() override; + void navigateToTokenTarget(uint64_t addr) override; + bool navigateToTokenGotoLabel(uint64_t label) override; + void defineNameForSelectedToken() override; + void editCommentForSelectedToken() override; + void setGraphInternal(FlowGraphRef graph, BinaryNinja::Ref entry, bool useAddr, uint64_t addr, bool notify, bool recenterWithPreviousGraph, size_t index = BN_INVALID_EXPR); @@ -221,7 +233,7 @@ class BINARYNINJAUIAPI FlowGraphWidget : void moveToEndOfView(); void selectAll(); void selectNone(); - void navigateToHighlightedToken(); + void activateHighlightedToken(); std::optional addressForCall(); uint64_t getTokenAddress(); diff --git a/ui/linearview.h b/ui/linearview.h index 4c3f8455ce..6914a5e1a1 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -16,6 +16,7 @@ #include "instructionedit.h" #include "ilchooser.h" #include "commands.h" +#include "tokennavigation.h" #include #define LINEAR_VIEW_UPDATE_CHECK_INTERVAL 200 @@ -156,7 +157,11 @@ class StickyHeader: public QWidget \ingroup linearview */ -class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, public BinaryNinja::BinaryDataNotification +class BINARYNINJAUIAPI LinearView : + public QAbstractScrollArea, + public View, + public TokenNavigationHandler, + public BinaryNinja::BinaryDataNotification { Q_OBJECT @@ -279,7 +284,22 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub FunctionRef func, uint64_t offset, size_t instrIndex, bool center, bool updateHighlight, bool navByRef = false); bool navigateToGotoLabel(uint64_t label); bool navigateToMatchingBrace(); - bool navigateToExternalLink(uint64_t linkSourceAddr); + + bool getLineForCursorPosition(LinearViewLine& result); + + BinaryViewRef getBinaryViewForTokenActivation() override; + QWidget* getWidgetForTokenActivation() override; + View* getViewForTokenActivation() override; + ViewFrame* getViewFrameForTokenActivation() override; + FunctionRef getFunctionForTokenActivation() override; + std::optional> getTokensForLineWithSelection() override; + TokenActivationAction getActionForLineWithSelection() override; + void navigateToTokenTarget(uint64_t addr) override; + bool navigateToTokenGotoLabel(uint64_t label) override; + bool navigateToTokenMatchingBrace() override; + void defineNameForSelectedToken() override; + void editCommentForSelectedToken() override; + void setSelectionOffsetsInternal(BNAddressRange range, bool navigateToStart = true); void viewData(); @@ -577,7 +597,7 @@ private Q_SLOTS: void moveToEndOfView(); void selectNone(); void selectAll(); - void navigateToHighlightedToken(); + void activateHighlightedToken(); void splitToNewTabAndNavigateFromCursorPosition(); void splitToNewWindowAndNavigateFromCursorPosition(); void splitToNewPaneAndNavigateFromCursorPosition(); diff --git a/ui/tokenizedtextview.h b/ui/tokenizedtextview.h index 9eca9e9744..e4a2fe1662 100644 --- a/ui/tokenizedtextview.h +++ b/ui/tokenizedtextview.h @@ -8,6 +8,7 @@ #include "commentdialog.h" #include "menus.h" #include "uicontext.h" +#include "tokennavigation.h" /*! @@ -45,6 +46,7 @@ class BINARYNINJAUIAPI TokenizedTextViewHistoryEntry : public HistoryEntry class BINARYNINJAUIAPI TokenizedTextView : public QAbstractScrollArea, public View, + public TokenNavigationHandler, public BinaryNinja::BinaryDataNotification { Q_OBJECT @@ -97,6 +99,14 @@ class BINARYNINJAUIAPI TokenizedTextView : void comment(); void commentAccepted(); + BinaryViewRef getBinaryViewForTokenActivation() override; + QWidget* getWidgetForTokenActivation() override; + View* getViewForTokenActivation() override; + FunctionRef getFunctionForTokenActivation() override; + void navigateToTokenTarget(uint64_t addr) override; + void defineNameForSelectedToken() override; + void editCommentForSelectedToken() override; + void convertToNop(); void alwaysBranch(); void invertBranch(); diff --git a/ui/tokennavigation.h b/ui/tokennavigation.h new file mode 100644 index 0000000000..9b59225cfd --- /dev/null +++ b/ui/tokennavigation.h @@ -0,0 +1,184 @@ +#pragma once + +#include "uitypes.h" +#include "action.h" +#include "binaryninjaapi.h" + +#include +#include +#include + +class View; +class ViewFrame; + +/*! + + \defgroup tokennavigation TokenNavigation + \ingroup uiapi +*/ + +/*! + The kind of behavior that activating a token (by double-clicking it, or by pressing the + "Activate Selection" key) should perform. + + \ingroup tokennavigation +*/ +enum TokenActivationType +{ + /*! Nothing can be done with the selected token */ + NoTokenActivation, + /*! Navigate to an address in the binary */ + NavigateToAddressActivation, + /*! Show a named type, optionally scrolling to a member at a given offset */ + NavigateToTypeActivation, + /*! Navigate to the line that defines a goto label */ + NavigateToGotoLabelActivation, + /*! Navigate to the brace matching the selected one */ + NavigateToMatchingBraceActivation, + /*! Rename the selected variable */ + DefineNameActivation, + /*! Edit the selected comment */ + EditCommentActivation +}; + +/*! + The result of resolving a token into the behavior that activating it should perform. + + \ingroup tokennavigation +*/ +struct BINARYNINJAUIAPI TokenActivationAction +{ + TokenActivationType type = NoTokenActivation; + /*! Target address for NavigateToAddressActivation, or the label identifier for + NavigateToGotoLabelActivation */ + uint64_t address = 0; + /*! Name of the type to show for NavigateToTypeActivation */ + std::string typeName; + /*! Offset of the member to show for NavigateToTypeActivation */ + uint64_t typeOffset = 0; + + bool isValid() const { return type != NoTokenActivation; } + /*! True for the actions that move the view somewhere, as opposed to those that edit the + database. Used to decide whether an entry point that only navigates should act. */ + bool isNavigation() const; + /*! True for actions that navigate to an address. */ + bool isNavigationToAddress() const; +}; + +/*! + Look up the derived string that a token refers to, if any. Only tokens rendered with + DerivedStringReferenceTokenContext refer to a derived string. + + \ingroup tokennavigation +*/ + +std::optional BINARYNINJAUIAPI getDerivedStringForToken( + FunctionRef func, const BinaryNinja::InstructionTextToken& token); + +/*! + Get the address that a string token refers to. For a derived string with a known location, + this is the location backing the string (which is not where the token itself is rendered). + + \ingroup tokennavigation +*/ +uint64_t BINARYNINJAUIAPI getAddressForStringToken(FunctionRef func, const BinaryNinja::InstructionTextToken& token); + +/*! + If the given address is an external symbol that is backed by another file in the project, + open that file at the corresponding location. + + \ingroup tokennavigation +*/ +bool BINARYNINJAUIAPI navigateToExternalLinkForAddress(BinaryViewRef data, uint64_t linkSourceAddr); + +/*! + Find the single token on a line that can be navigated to, for use when no individual token is + selected. Tokens are searched for in order of preference, and a category containing more than + one candidate is treated as ambiguous rather than picking one arbitrarily. + + \ingroup tokennavigation +*/ +std::optional BINARYNINJAUIAPI getNavigationTargetForLineTokens( + const std::vector& tokens); + +/*! + Shared implementation of "activate the selected token", used by every view that renders + tokens so that double-clicking a token and pressing the "Activate Selection" key behave + identically everywhere. + + Views inherit from this alongside their widget and View base classes, provide the context + that token resolution needs, and implement the handful of operations that are view specific. + + \ingroup tokennavigation +*/ +class BINARYNINJAUIAPI TokenNavigationHandler +{ +public: + virtual ~TokenNavigationHandler() = default; + + /*! Resolve a token into the action that activating it should perform. Returns an invalid + action if the token is not one that can be activated. */ + TokenActivationAction getActionForToken(const HighlightTokenState& highlight); + + /*! Resolve the current selection into an action, falling back to the contents of the + current line when no single token is selected. */ + TokenActivationAction getActionForSelection(const HighlightTokenState& highlight); + + /*! Perform the action for the current selection. If navigationOnly is set, actions that + edit the database (renaming a variable, editing a comment) are not performed. Returns + true if an action was performed. */ + bool activateSelectedToken(const HighlightTokenState& highlight, bool navigationOnly = false); + + /*! Determine whether activateSelectedToken would do anything, for use as the validity + check of an action. */ + bool canActivateSelectedToken(const HighlightTokenState& highlight, bool navigationOnly = false); + + /*! Get the target address for the current selection if it is an action that navigates + to an address. */ + std::optional getNavigationAddressForSelectedToken(const HighlightTokenState& highlight); + + /*! Determine whether getNavigationAddressForSelectedToken would return an address to + navigate to, for use as the validity check of an action. */ + bool canNavigateToAddressForSelectedToken(const HighlightTokenState& highlight); + +protected: + /*! The binary view that the tokens were rendered from */ + virtual BinaryViewRef getBinaryViewForTokenActivation() = 0; + /*! The widget for this view */ + virtual QWidget* getWidgetForTokenActivation() = 0; + /*! The view to navigate */ + virtual View* getViewForTokenActivation() = 0; + /*! The context that owns the view */ + virtual UIContext* getUIContextForTokenActivation(); + /*! The view frame that owns the view */ + virtual ViewFrame* getViewFrameForTokenActivation(); + /*! The function that the tokens were rendered from, if any. Required to resolve tokens that + refer to an IL expression, such as derived strings. */ + virtual FunctionRef getFunctionForTokenActivation() { return nullptr; } + /*! The tokens of the line containing the selection, used when no single token is selected */ + virtual std::optional> getTokensForLineWithSelection() + { + return std::nullopt; + } + /*! An action to fall back on when neither the selected token nor the tokens on the line + resolve to anything, such as navigating to the function that a header line describes */ + virtual TokenActivationAction getActionForLineWithSelection() { return TokenActivationAction(); } + + /*! Navigate to an address within this view, or to wherever the user's preferences say that + the address should be shown */ + virtual void navigateToTokenTarget(uint64_t addr) = 0; + /*! Navigate to the line defining a goto label. Views without goto labels need not implement + this. */ + virtual bool navigateToTokenGotoLabel(uint64_t label) + { + (void)label; + return false; + } + /*! Navigate to the brace matching the selected one. Views without braces need not implement + this. */ + virtual bool navigateToTokenMatchingBrace() { return false; } + /*! Rename the selected variable */ + virtual void defineNameForSelectedToken() {} + /*! Edit the selected comment */ + virtual void editCommentForSelectedToken() {} +};