diff options
| -rw-r--r-- | binaryninjaapi.h | 17 | ||||
| -rw-r--r-- | binaryninjacore.h | 22 | ||||
| -rw-r--r-- | function.cpp | 42 | ||||
| -rw-r--r-- | highlevelil.cpp | 38 | ||||
| -rw-r--r-- | highlevelilinstruction.cpp | 19 | ||||
| -rw-r--r-- | highlevelilinstruction.h | 12 | ||||
| -rw-r--r-- | lang/c/pseudoc.cpp | 214 | ||||
| -rw-r--r-- | lang/rust/pseudorust.cpp | 2 | ||||
| -rw-r--r-- | ui/linearview.h | 1 | ||||
| -rw-r--r-- | ui/render.h | 2 |
10 files changed, 293 insertions, 76 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 884861bb..bcaf644e 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -4367,6 +4367,7 @@ namespace BinaryNinja { class TypeLibrary; class TypeArchive; class MemoryMap; + struct HighLevelILInstruction; class QueryMetadataException : public ExceptionWithStackTrace { @@ -10501,6 +10502,8 @@ namespace BinaryNinja { { int m_advancedAnalysisRequests; + bool IsRegionCollapsed(uint64_t hash) const; + public: Function(BNFunction* func); virtual ~Function(); @@ -11094,6 +11097,14 @@ namespace BinaryNinja { Confidence<bool> IsInlinedDuringAnalysis(); void SetAutoInlinedDuringAnalysis(Confidence<bool> inlined); void SetUserInlinedDuringAnalysis(Confidence<bool> inlined); + + // TODO: Documentation + bool IsInstructionCollapsed(const HighLevelILInstruction& instr, uint64_t designator = 0) const; + bool IsCollapsed() const; + void ToggleRegion(uint64_t hash); + void CollapseRegion(uint64_t hash); + void ExpandRegion(uint64_t hash); + void ExpandAll(); }; /*! @@ -18445,6 +18456,12 @@ namespace BinaryNinja { InstructionTextToken::FreeInstructionTextToken(&converted); } + void PrependCollapseIndicator(); + void PrependCollapseIndicator(Ref<Function> function, const HighLevelILInstruction& instr, uint64_t designator = 0); + void PrependCollapseIndicator(BNInstructionTextTokenContext context, uint64_t hash); + bool HasCollapsableRegions(); + void SetHasCollapsableRegions(bool state); + /*! Starts a new line in the output. */ void NewLine(); diff --git a/binaryninjacore.h b/binaryninjacore.h index f2083006..10ad516f 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -403,7 +403,9 @@ extern "C" IndirectImportToken = 69, ExternalSymbolToken = 70, StackVariableToken = 71, - AddressSeparatorToken = 72 + AddressSeparatorToken = 72, + CollapsedInformationToken = 73, + CollapseStateIndicatorToken = 74 } BNInstructionTextTokenType; typedef enum BNInstructionTextTokenContext @@ -418,7 +420,10 @@ extern "C" ConstStringDataTokenContext = 7, // For ConstData strings StringReferenceTokenContext = 8, // For References to strings StringDataVariableTokenContext = 9, // For String DataVariables - StringDisplayTokenContext = 10 // For displaying strings which aren't associated with an address + StringDisplayTokenContext = 10, // For displaying strings which aren't associated with an address + ContentCollapsedContext = 11, + ContentExpandedContext = 12, + ContentCollapsiblePadding = 13 } BNInstructionTextTokenContext; typedef enum BNLinearDisassemblyLineType @@ -442,7 +447,8 @@ extern "C" SectionEndLineType, SectionSeparatorLineType, NonContiguousSeparatorLineType, - AnalysisWarningLineType + AnalysisWarningLineType, + CollapsedFunctionEndLineType } BNLinearDisassemblyLineType; typedef enum BNTokenEscapingType @@ -4830,6 +4836,12 @@ extern "C" BINARYNINJACOREAPI uint64_t* BNGetUnresolvedIndirectBranches(BNFunction* func, size_t* count); BINARYNINJACOREAPI bool BNHasUnresolvedIndirectBranches(BNFunction* func); + BINARYNINJACOREAPI void BNFunctionToggleRegion(BNFunction* func, uint64_t hash); + BINARYNINJACOREAPI bool BNFunctionIsRegionCollapsed(BNFunction* func, uint64_t hash); + BINARYNINJACOREAPI void BNFunctionExpandAll(BNFunction* func); + BINARYNINJACOREAPI void BNFunctionCollapseRegion(BNFunction* func, uint64_t hash); + BINARYNINJACOREAPI void BNFunctionExpandRegion(BNFunction* func, uint64_t hash); + BINARYNINJACOREAPI void BNSetAutoCallTypeAdjustment( BNFunction* func, BNArchitecture* arch, uint64_t addr, BNTypeWithConfidence* type); BINARYNINJACOREAPI void BNSetUserCallTypeAdjustment( @@ -7860,6 +7872,10 @@ extern "C" // High level token emitter BINARYNINJACOREAPI BNHighLevelILTokenEmitter* BNNewHighLevelILTokenEmitterReference(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenPrependCollapseBlankIndicator(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenPrependCollapseIndicator(BNHighLevelILTokenEmitter* emitter, BNInstructionTextTokenContext context, uint64_t hash); + BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasCollapsableRegions(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetHasCollapsableRegions(BNHighLevelILTokenEmitter* emitter, bool state); BINARYNINJACOREAPI void BNFreeHighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter); BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppend(BNHighLevelILTokenEmitter* emitter, BNInstructionTextToken* token); BINARYNINJACOREAPI void BNHighLevelILTokenEmitterNewLine(BNHighLevelILTokenEmitter* emitter); diff --git a/function.cpp b/function.cpp index 33cb5e52..ab441193 100644 --- a/function.cpp +++ b/function.cpp @@ -20,6 +20,7 @@ #include "binaryninjaapi.h" #include "mediumlevelilinstruction.h" +#include "highlevelilinstruction.h" #include <cstring> using namespace BinaryNinja; @@ -3261,6 +3262,47 @@ void Function::SetUserInlinedDuringAnalysis(Confidence<bool> inlined) } +void Function::ToggleRegion(uint64_t hash) +{ + BNFunctionToggleRegion(m_object, hash); +} + + +void Function::CollapseRegion(uint64_t hash) +{ + BNFunctionCollapseRegion(m_object, hash); +} + + +void Function::ExpandRegion(uint64_t hash) +{ + BNFunctionExpandRegion(m_object, hash); +} + + +bool Function::IsCollapsed() const +{ + return IsRegionCollapsed(GetStart()); +} + + +bool Function::IsInstructionCollapsed(const HighLevelILInstruction& instr, uint64_t designator) const +{ + return IsRegionCollapsed(instr.GetInstructionHash(designator)); +} + + +bool Function::IsRegionCollapsed(uint64_t hash) const +{ + return BNFunctionIsRegionCollapsed(m_object, hash); +} + + +void Function::ExpandAll() +{ + BNFunctionExpandAll(m_object); +} + AdvancedFunctionAnalysisDataRequestor::AdvancedFunctionAnalysisDataRequestor(Function* func) : m_func(func) { if (m_func) diff --git a/highlevelil.cpp b/highlevelil.cpp index 89cf645f..10e2cd6a 100644 --- a/highlevelil.cpp +++ b/highlevelil.cpp @@ -648,6 +648,42 @@ HighLevelILTokenEmitter::HighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emit m_object = emitter; } +void HighLevelILTokenEmitter::PrependCollapseIndicator() +{ + BNHighLevelILTokenPrependCollapseBlankIndicator(m_object); +} + + +void HighLevelILTokenEmitter::PrependCollapseIndicator(Ref<Function> function, const HighLevelILInstruction& instr, uint64_t designator) +{ + if (!HasCollapsableRegions()) + return; + + // Insert the collapse indicator at the beginning of the line if one isn't already there or the + // one that is there is empty + auto context = HighLevelILInstruction::CanCollapse(instr.operation) ? + (function && function->IsInstructionCollapsed(instr, designator) ? ContentCollapsedContext : ContentExpandedContext) : + ContentCollapsiblePadding; + + PrependCollapseIndicator(context, instr.GetInstructionHash(designator)); +} + +void HighLevelILTokenEmitter::PrependCollapseIndicator(BNInstructionTextTokenContext context, uint64_t hash) +{ + BNHighLevelILTokenPrependCollapseIndicator(m_object, context, hash); +} + +bool HighLevelILTokenEmitter::HasCollapsableRegions() +{ + return BNHighLevelILTokenEmitterHasCollapsableRegions(m_object); +} + + +void HighLevelILTokenEmitter::SetHasCollapsableRegions(bool state) +{ + BNHighLevelILTokenEmitterSetHasCollapsableRegions(m_object, state); +} + void HighLevelILTokenEmitter::NewLine() { @@ -908,4 +944,4 @@ void HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( newNames.push_back(names[i]); BNFreeStringList(names, nameCount); nameList.insert(nameList.begin(), newNames.begin(), newNames.end()); -} +}
\ No newline at end of file diff --git a/highlevelilinstruction.cpp b/highlevelilinstruction.cpp index c27e1251..7f764009 100644 --- a/highlevelilinstruction.cpp +++ b/highlevelilinstruction.cpp @@ -2480,6 +2480,25 @@ size_t HighLevelILInstruction::GetDestMemoryVersion() const } +bool HighLevelILInstruction::CanCollapse(int operation) +{ + switch (operation) + { + case HLIL_IF: + case HLIL_WHILE: + case HLIL_WHILE_SSA: + case HLIL_DO_WHILE: + case HLIL_DO_WHILE_SSA: + case HLIL_FOR: + case HLIL_FOR_SSA: + case HLIL_SWITCH: + case HLIL_CASE: + return true; + default: + return false; + } +} + ExprId HighLevelILFunction::Nop(const ILSourceLocation& loc) { return AddExprWithLocation(HLIL_NOP, loc, 0); diff --git a/highlevelilinstruction.h b/highlevelilinstruction.h index be6f8b43..25c639ec 100644 --- a/highlevelilinstruction.h +++ b/highlevelilinstruction.h @@ -388,6 +388,17 @@ namespace BinaryNinja bool HasParent() const; HighLevelILInstruction GetParent() const; + uint64_t GetInstructionHash(size_t discriminator = 0) const + { + constexpr auto rotl = [](uint64_t value, int shift) + { return (value << shift) | (value >> (64 - shift)); }; + std::hash<uint64_t> hasher; + uint64_t hash = hasher(operation); + hash ^= rotl(hasher(address), 23); + hash ^= rotl(hasher(discriminator), 47); + + return hash; + } template <BNHighLevelILOperation N> HighLevelILInstructionAccessor<N>& As() @@ -764,6 +775,7 @@ namespace BinaryNinja size_t GetSourceMemoryVersion() const; HighLevelILIndexList GetSourceMemoryVersions() const; size_t GetDestMemoryVersion() const; + static bool CanCollapse(int operand); }; /*! diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp index b2dca522..2a1ac3a9 100644 --- a/lang/c/pseudoc.cpp +++ b/lang/c/pseudoc.cpp @@ -21,6 +21,8 @@ void PseudoCFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) // For switch cases in C, declarations inside aren't scoped by default. Add braces around the cases // to give them scope. tokens.SetBracesAroundSwitchCases(true); + + tokens.SetHasCollapsableRegions(true); } @@ -674,6 +676,9 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H tokens.Append(TextToken, " "); } + auto function = m_highLevelIL->GetFunction(); + if (instr.ast) + tokens.PrependCollapseIndicator(function, instr); switch (instr.operation) { case HLIL_BLOCK: @@ -744,11 +749,19 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H if (updateExpr.operation != HLIL_NOP) GetExprTextInternal(updateExpr, tokens, settings, asFullAst); tokens.AppendCloseParen(); - auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); - tokens.BeginScope(scopeType); - GetExprTextInternal(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - tokens.EndScope(scopeType); - tokens.FinalizeScope(); + if (function->IsInstructionCollapsed(instr)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } + } else { @@ -761,7 +774,8 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H break; case HLIL_IF: - [&]() { + [&]() + { const auto condExpr = instr.GetConditionExpr<HLIL_IF>(); const auto trueExpr = instr.GetTrueExpr<HLIL_IF>(); const auto falseExpr = instr.GetFalseExpr<HLIL_IF>(); @@ -773,11 +787,17 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H if (!asFullAst) return; - auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); - tokens.BeginScope(scopeType); - - GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - tokens.EndScope(scopeType); + if (function->IsInstructionCollapsed(instr)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + } //tokens.SetCurrentExpr(falseExpr); if (falseExpr.operation == HLIL_IF) { @@ -788,12 +808,20 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H else if (falseExpr.operation != HLIL_NOP) { tokens.ScopeContinuation(false); + tokens.PrependCollapseIndicator(function, instr, 1); tokens.Append(KeywordToken, "else"); - scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); - tokens.BeginScope(scopeType); - GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - tokens.EndScope(scopeType); - tokens.FinalizeScope(); + if (function->IsInstructionCollapsed(instr, 1)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + auto scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } } else { @@ -814,11 +842,18 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H if (!asFullAst) return; - auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); - tokens.BeginScope(scopeType); - GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); - tokens.EndScope(scopeType); - tokens.FinalizeScope(); + if (function->IsInstructionCollapsed(instr)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } }(); break; @@ -830,17 +865,31 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H if (asFullAst) { tokens.Append(KeywordToken, "do"); - auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); - tokens.BeginScope(scopeType); - GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); - tokens.EndScope(scopeType); - tokens.ScopeContinuation(true); - tokens.Append(KeywordToken, "while "); - tokens.AppendOpenParen(); - GetExprTextInternal(condExpr, tokens, settings); - tokens.AppendCloseParen(); - tokens.Append(KeywordToken, ";"); - tokens.FinalizeScope(); + if (function->IsInstructionCollapsed(instr)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + tokens.NewLine(); + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + tokens.Append(KeywordToken, ";"); + } + else + { + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + + tokens.ScopeContinuation(true); + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + tokens.Append(KeywordToken, ";"); + tokens.FinalizeScope(); + } } else { @@ -863,29 +912,43 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings, asFullAst); tokens.AppendCloseParen(); - tokens.BeginScope(SwitchScopeType); if (!asFullAst) return; - for (const auto caseExpr : caseExprs) + if (function->IsInstructionCollapsed(instr)) { - GetExprTextInternal(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - tokens.NewLine(); + tokens.Append(CollapsedInformationToken, " {...}"); } - - if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) + else { - tokens.Append(KeywordToken, "default"); - tokens.Append(TextToken, ":"); - tokens.BeginScope(CaseScopeType); - GetExprTextInternal(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - tokens.EndScope(CaseScopeType); + tokens.BeginScope(SwitchScopeType); + for (const auto caseExpr: caseExprs) + { + GetExprTextInternal(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.NewLine(); + } + + if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) + { + tokens.PrependCollapseIndicator(function, instr, 1); + tokens.Append(KeywordToken, "default"); + tokens.Append(TextToken, ":"); + if (function->IsInstructionCollapsed(instr, 1)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + tokens.BeginScope(CaseScopeType); + GetExprTextInternal(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + } + } + tokens.EndScope(SwitchScopeType); tokens.FinalizeScope(); } - tokens.EndScope(SwitchScopeType); - tokens.FinalizeScope(); - }(); break; @@ -907,43 +970,52 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H if (!asFullAst) return; - tokens.BeginScope(CaseScopeType); - GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + if (function->IsInstructionCollapsed(instr)) + { + tokens.Append(CollapsedInformationToken, " {...}"); + } + else + { + tokens.PrependCollapseIndicator(function, instr); + tokens.BeginScope(CaseScopeType); + GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); - static const std::vector<BNHighLevelILOperation> operations { - HLIL_CONTINUE, HLIL_NORET, HLIL_UNREACHABLE, HLIL_JUMP, HLIL_GOTO, HLIL_TAILCALL}; + static const std::vector<BNHighLevelILOperation> operations { + HLIL_CONTINUE, HLIL_NORET, HLIL_UNREACHABLE, HLIL_JUMP, HLIL_GOTO, HLIL_TAILCALL}; - // If the case doesn't have an instruction that implicitly exits the case, append a break statement - // at the end of the case - if (trueExpr.operation == HLIL_BLOCK) - { - const auto& blockExprs = trueExpr.GetBlockExprs<HLIL_BLOCK>(); - bool disallowedOperation = false; - for (const auto expr : blockExprs) + // If the case doesn't have an instruction that implicitly exits the case, append a break statement + // at the end of the case + if (trueExpr.operation == HLIL_BLOCK) { - if (std::find(operations.begin(), operations.end(), expr.operation) != operations.end()) + const auto& blockExprs = trueExpr.GetBlockExprs<HLIL_BLOCK>(); + bool disallowedOperation = false; + for (const auto expr : blockExprs) { - disallowedOperation = true; - break; + if (std::find(operations.begin(), operations.end(), expr.operation) != operations.end()) + { + disallowedOperation = true; + break; + } + } + if (!disallowedOperation) + { + tokens.NewLine(); + tokens.PrependCollapseIndicator(); + tokens.Append(KeywordToken, "break"); + tokens.Append(TextToken, ";"); } } - - if (!disallowedOperation) + else if (std::find(operations.begin(), operations.end(), trueExpr.operation) == operations.end()) { tokens.NewLine(); + tokens.PrependCollapseIndicator(); tokens.Append(KeywordToken, "break"); tokens.Append(TextToken, ";"); } - } - else if (std::find(operations.begin(), operations.end(), trueExpr.operation) == operations.end()) - { - tokens.NewLine(); - tokens.Append(KeywordToken, "break"); - tokens.Append(TextToken, ";"); - } - tokens.EndScope(CaseScopeType); - tokens.FinalizeScope(); + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + } }(); break; diff --git a/lang/rust/pseudorust.cpp b/lang/rust/pseudorust.cpp index 278bbaa8..83e2c39d 100644 --- a/lang/rust/pseudorust.cpp +++ b/lang/rust/pseudorust.cpp @@ -27,6 +27,8 @@ void PseudoRustFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) // Rust doesn't allow omitting the braces around conditional bodies tokens.SetSimpleScopeAllowed(false); + + tokens.SetHasCollapsableRegions(false); } diff --git a/ui/linearview.h b/ui/linearview.h index 1c470cb6..05700e70 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -202,6 +202,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub bool m_cursorAscii; bool m_tokenSelection = false; HighlightTokenState m_highlight; + bool m_displayCollapseIndicators = false; uint64_t m_navByRefTarget; bool m_navByRef = false; bool m_doubleClickLatch = false; diff --git a/ui/render.h b/ui/render.h index 39393e4c..0137f8a5 100644 --- a/ui/render.h +++ b/ui/render.h @@ -122,7 +122,7 @@ class BINARYNINJAUIAPI RenderContext QPainter& p, BNLinearDisassemblyLineType type, const QRect& rect, const QRect& dirtyRect, int gutterWidth); int drawDisassemblyLine(QPainter& p, int left, int top, const std::vector<BinaryNinja::InstructionTextToken>& tokens, HighlightTokenState& highlight, - bool highlightOnly = false) const; + bool highlightOnly = false, bool renderCollapseIndicator = false) const; void drawHexEditorLine(QPainter& p, int left, int top, const HexEditorHighlightState& highlight, BinaryViewRef view, uint64_t lineStartAddr, size_t cols, size_t firstCol, size_t count, bool cursorVisible, bool cursorAscii, |
