summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h17
-rw-r--r--binaryninjacore.h22
-rw-r--r--function.cpp42
-rw-r--r--highlevelil.cpp38
-rw-r--r--highlevelilinstruction.cpp19
-rw-r--r--highlevelilinstruction.h12
-rw-r--r--lang/c/pseudoc.cpp214
-rw-r--r--lang/rust/pseudorust.cpp2
-rw-r--r--ui/linearview.h1
-rw-r--r--ui/render.h2
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,