summaryrefslogtreecommitdiff
path: root/lang
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-08-22 12:55:49 -0600
committerRusty Wagner <rusty.wagner@gmail.com>2024-10-21 13:56:55 -0400
commitd8e3001e535fad178c621ff07418f81f25123dc4 (patch)
tree54c03cef2f0bdfd9a3b6df4334c81becb63e4993 /lang
parent0e281a30d73c0f31ef9442fef0346779164231ad (diff)
Allow multiple high level representations for display, add Pseudo Rust and a Pseudo Python example plugin
Diffstat (limited to 'lang')
-rw-r--r--lang/c/CMakeLists.txt45
-rw-r--r--lang/c/pseudoc.cpp2852
-rw-r--r--lang/c/pseudoc.h66
-rw-r--r--lang/rust/CMakeLists.txt45
-rw-r--r--lang/rust/pseudorust.cpp2925
-rw-r--r--lang/rust/pseudorust.h79
-rw-r--r--lang/rust/rusttypes.cpp339
-rw-r--r--lang/rust/rusttypes.h32
8 files changed, 6383 insertions, 0 deletions
diff --git a/lang/c/CMakeLists.txt b/lang/c/CMakeLists.txt
new file mode 100644
index 00000000..9ccc89f2
--- /dev/null
+++ b/lang/c/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(lang_pseudoc)
+
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.h)
+
+if(DEMO)
+ add_library(lang_pseudoc STATIC ${SOURCES})
+else()
+ add_library(lang_pseudoc SHARED ${SOURCES})
+endif()
+
+target_include_directories(lang_pseudoc
+ PRIVATE ${PROJECT_SOURCE_DIR})
+
+if(WIN32)
+ target_link_directories(lang_pseudoc
+ PRIVATE ${BN_INSTALL_DIR})
+ target_link_libraries(lang_pseudoc binaryninjaapi binaryninjacore)
+else()
+ target_link_libraries(lang_pseudoc binaryninjaapi)
+endif()
+
+set_target_properties(lang_pseudoc PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(lang_pseudoc)
+ set_target_properties(lang_pseudoc PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp
new file mode 100644
index 00000000..b2dca522
--- /dev/null
+++ b/lang/c/pseudoc.cpp
@@ -0,0 +1,2852 @@
+#include <inttypes.h>
+#include "pseudoc.h"
+#include "highlevelilinstruction.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+PseudoCFunction::PseudoCFunction(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) :
+ LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction)
+{
+}
+
+
+void PseudoCFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens)
+{
+ // Braces must always be turned on for C
+ tokens.SetBraceRequirement(BracesAlwaysRequired);
+
+ // For switch cases in C, declarations inside aren't scoped by default. Add braces around the cases
+ // to give them scope.
+ tokens.SetBracesAroundSwitchCases(true);
+}
+
+
+void PseudoCFunction::BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens)
+{
+ if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex)
+ {
+ // At top level, add braces around the entire function
+ tokens.AppendOpenBrace();
+ tokens.NewLine();
+ tokens.IncreaseIndent();
+ }
+}
+
+
+void PseudoCFunction::EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens)
+{
+ if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex)
+ {
+ // At top level, add braces around the entire function
+ tokens.NewLine();
+ tokens.DecreaseIndent();
+ tokens.AppendCloseBrace();
+ }
+}
+
+
+BNSymbolDisplayResult PseudoCFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence)
+{
+ Confidence<Ref<Type>> type = instr.GetType();
+ if (type && (type->GetClass() == PointerTypeClass) && type->IsConst())
+ {
+ string stringValue;
+ size_t childWidth = 0;
+ if (auto child = type->GetChildType(); child)
+ childWidth = child->GetWidth();
+ if (auto strType = GetFunction()->GetView()->CheckForStringAnnotationType(val, stringValue, false, false, childWidth); strType.has_value())
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(BraceToken, DisassemblyTextRenderer::GetStringLiteralPrefix(strType.value()) + string("\""));
+ tokens.emplace_back(StringToken, StringReferenceTokenContext, stringValue, instr.address, strType.value());
+ tokens.emplace_back(BraceToken, "\"");
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+ }
+
+ if (GetFunction())
+ {
+ // If the pointer has a value of 0, check if it points to a valid address by
+ // 1. If the binary is relocatable, assign the pointer as nullptr
+ // 2. else, check if the constant zero which being referenced is a pointer(display as symbol) or not(display as nullptr)
+ if(val == 0x0 && type && (type->GetClass() == PointerTypeClass))
+ {
+ if (GetFunction()->GetView()->IsRelocatable())
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val);
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+
+ auto arch = GetHighLevelILFunction()->GetArchitecture();
+ auto refs = GetHighLevelILFunction()->GetFunction()->GetConstantsReferencedByInstructionIfAvailable(
+ arch, instr.address);
+ bool constantZeroBeingReferencedIsPointer = false;
+
+ for (const BNConstantReference& ref : refs)
+ if (ref.value == 0x0 && ref.pointer)
+ constantZeroBeingReferencedIsPointer = true;
+ if (!constantZeroBeingReferencedIsPointer)
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val);
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+ }
+
+ Ref<BinaryView> data = GetFunction()->GetView();
+ vector<InstructionTextToken> symTokens;
+ BNSymbolDisplayResult result = DisassemblyTextRenderer::AddSymbolTokenStatic(symTokens, val, 0,
+ BN_INVALID_OPERAND, data, settings ? settings->GetMaximumSymbolWidth() : 0, GetFunction(),
+ BN_FULL_CONFIDENCE, symbolDisplay, precedence, instr.address);
+ if (result != NoSymbolAvailable)
+ {
+ for (auto& i : symTokens)
+ tokens.emplace_back(i);
+ return result;
+ }
+ }
+
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+
+ tokens.emplace_back(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(TypeNameToken, GetSizeToken(instr.size, false));
+ tokens.emplace_back(OperationToken, "*");
+ tokens.emplace_back(BraceToken, ")");
+ }
+ }
+
+ char valStr[32];
+ if (val >= 0)
+ {
+ if (val <= 9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val);
+ }
+ else
+ {
+ if (val >= -9)
+ snprintf(valStr, sizeof(valStr), "-%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "-0x%" PRIx64, val);
+ }
+
+ tokens.emplace_back(PossibleAddressToken, InstructionAddressTokenContext, valStr, instr.address, val);
+
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+}
+
+
+string PseudoCFunction::GetSizeToken(size_t size, bool isSigned)
+{
+ char sizeStr[32];
+
+ switch (size)
+ {
+ case 0:
+ return {};
+ case 1:
+ return (isSigned ? "int8_t" : "uint8_t");
+ case 2:
+ return (isSigned ? "int16_t" : "uint16_t");
+ case 4:
+ return (isSigned ? "int32_t" : "uint32_t");
+ case 8:
+ return (isSigned ? "int64_t" : "uint64_t");
+ case 10:
+ return (isSigned ? "int80_t" : "uint80_t");
+ case 16:
+ return (isSigned ? "int128_t" : "uint128_t");
+ }
+
+ snprintf(sizeStr, sizeof(sizeStr), "%sint%" PRIuPTR "_t", isSigned ? "" : "u", size);
+ return {sizeStr};
+}
+
+
+void PseudoCFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter)
+{
+ const auto token = GetSizeToken(size, isSigned);
+ if (!token.empty())
+ emitter.Append(TypeNameToken, token);
+}
+
+
+void PseudoCFunction::AppendSingleSizeToken(
+ size_t size, BNInstructionTextTokenType type, HighLevelILTokenEmitter& emitter)
+{
+ char sizeStr[32];
+
+ switch (size)
+ {
+ case 0:
+ break;
+ case 1:
+ emitter.Append(type, "B");
+ break;
+ case 2:
+ emitter.Append(type, "W");
+ break;
+ case 4:
+ emitter.Append(type, "D");
+ break;
+ case 8:
+ emitter.Append(type, "Q");
+ break;
+ case 10:
+ emitter.Append(type, "T");
+ break;
+ case 16:
+ emitter.Append(type, "O");
+ break;
+ default:
+ snprintf(sizeStr, sizeof(sizeStr), "%" PRIuPTR "", size);
+ emitter.Append(type, sizeStr);
+ break;
+ }
+}
+
+
+void PseudoCFunction::AppendComparison(const string& comparison, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+
+ GetExprTextInternal(leftExpr, emitter, settings, asFullAst, precedence, false, signedHint);
+ emitter.Append(OperationToken, comparison);
+ GetExprTextInternal(rightExpr, emitter, settings, asFullAst, precedence, false, signedHint);
+}
+
+
+void PseudoCFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto& twoOperand = instr.AsTwoOperand();
+ const auto leftExpr = twoOperand.GetLeftExpr();
+ const auto rightExpr = twoOperand.GetRightExpr();
+ BNOperatorPrecedence leftPrecedence = precedence;
+ switch (precedence)
+ {
+ case SubOperatorPrecedence:
+ // Treat left side of subtraction as same level as addition. This lets
+ // (a - b) - c be represented as a - b - c, but a - (b - c) does not
+ // simplify at rendering
+ leftPrecedence = AddOperatorPrecedence;
+ break;
+ case DivideOperatorPrecedence:
+ // Treat left side of divison as same level as multiplication. This lets
+ // (a / b) / c be represented as a / b / c, but a / (b / c) does not
+ // simplify at rendering
+ leftPrecedence = MultiplyOperatorPrecedence;
+ break;
+ default:
+ break;
+ }
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ emitter.Append(OperationToken, "COMBINE");
+ emitter.AppendOpenParen();
+ GetExprTextInternal(high, emitter, settings, asFullAst);
+ emitter.Append(TextToken, ", ");
+ GetExprTextInternal(low, emitter, settings, asFullAst);
+ emitter.AppendCloseParen();
+ }
+
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ if (leftExpr.operation == HLIL_VAR && (operand == " + " || operand == " - "))
+ {
+ const auto variableType = GetFunction()->GetVariableType(leftExpr.GetVariable());
+ if (variableType)
+ {
+ const auto childType = variableType->GetChildType();
+ if (variableType->IsPointer() && childType && childType->GetWidth() != 1)
+ {
+ emitter.AppendOpenParen();
+ emitter.Append(TypeNameToken, "char");
+ emitter.Append(TextToken, "*");
+ emitter.AppendCloseParen();
+ }
+ }
+ }
+ }
+
+ GetExprTextInternal(leftExpr, emitter, settings, asFullAst, leftPrecedence, false, signedHint);
+
+ auto lessThanZero = [](uint64_t value, uint64_t width) -> bool {
+ return ((1UL << ((width * 8) - 1UL)) & value) != 0;
+ };
+
+ if ((operand == " + ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) &&
+ rightExpr.size >= leftExpr.size)
+ {
+ // Convert addition of a negative constant into subtraction of a positive constant
+ emitter.Append(OperationToken, " - ");
+ emitter.AppendIntegerTextToken(
+ rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+ if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) &&
+ rightExpr.size >= leftExpr.size)
+ {
+ // Convert subtraction of a negative constant into addition of a positive constant
+ emitter.Append(OperationToken, " + ");
+ emitter.AppendIntegerTextToken(
+ rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+
+ emitter.Append(OperationToken, operand);
+ GetExprTextInternal(rightExpr, emitter, settings, asFullAst, precedence, false, signedHint);
+}
+
+
+void PseudoCFunction::AppendTwoOperandFunction(const string& function,
+ const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings,
+ bool sizeToken)
+{
+ const auto& twoOperand = instr.AsTwoOperand();
+ const auto leftExpr = twoOperand.GetLeftExpr();
+ const auto rightExpr = twoOperand.GetRightExpr();
+
+ emitter.Append(OperationToken, function);
+ if (sizeToken)
+ AppendSingleSizeToken(twoOperand.size, OperationToken, emitter);
+ emitter.AppendOpenParen();
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ emitter.Append(OperationToken, "COMBINE");
+ emitter.AppendOpenParen();
+ GetExprTextInternal(high, emitter, settings);
+ emitter.Append(TextToken, ", ");
+ GetExprTextInternal(low, emitter, settings);
+ emitter.AppendCloseParen();
+ }
+ else
+ {
+ GetExprTextInternal(leftExpr, emitter, settings);
+ }
+
+ emitter.Append(TextToken, ", ");
+ GetExprTextInternal(rightExpr, emitter, settings);
+
+ emitter.AppendCloseParen();
+}
+
+
+void PseudoCFunction::AppendTwoOperandFunctionWithCarry(const string& function,
+ const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+ const auto carryExpr = instr.GetCarryExpr();
+
+ tokens.Append(OperationToken, function);
+ AppendSingleSizeToken(instr.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ tokens.Append(OperationToken, "COMBINE");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(high, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprTextInternal(low, tokens, settings);
+ tokens.AppendCloseParen();
+ }
+
+ GetExprTextInternal(leftExpr, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprTextInternal(rightExpr, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprTextInternal(carryExpr, tokens, settings);
+
+ tokens.AppendCloseParen();
+}
+
+
+Ref<Type> PseudoCFunction::GetFieldType(const HighLevelILInstruction& var, bool deref)
+{
+ Ref<Type> type = var.GetType().GetValue();
+ if (deref && type && (type->GetClass() == PointerTypeClass))
+ type = type->GetChildType().GetValue();
+
+ if (type && (type->GetClass() == NamedTypeReferenceClass))
+ type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference());
+
+ return type;
+}
+
+
+PseudoCFunction::FieldDisplayType PseudoCFunction::GetFieldDisplayType(
+ Ref<Type> type, uint64_t offset, size_t memberIndex, bool deref)
+{
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure*, size_t, uint64_t, uint64_t, const StructureMember&) {}),
+ memberIndexHint)
+ return FieldDisplayName;
+ return FieldDisplayOffset;
+ }
+ else if (deref || offset != 0)
+ return FieldDisplayMemberOffset;
+ else
+ return FieldDisplayNone;
+}
+
+
+void PseudoCFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset,
+ size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref)
+{
+ const auto type = GetFieldType(var, deref);
+ const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref);
+ switch (fieldDisplayType)
+ {
+ case FieldDisplayName:
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ if (deref && displayDeref)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ deref = false;
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0,
+ BN_FULL_CONFIDENCE, nameList);
+ }),
+ memberIndexHint)
+ return;
+
+ // Part of structure but no defined field, use __offset syntax
+ if (deref && displayDeref)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ char offsetStr[64];
+ snprintf(
+ offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", offset, Type::GetSizeSuffix(size).c_str());
+
+ vector<string> nameList {offsetStr};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList);
+ return;
+ }
+
+ case FieldDisplayOffset:
+ {
+ /* this is handled before the display */
+ return;
+ }
+
+ case FieldDisplayMemberOffset:
+ {
+ tokens.AppendOpenBracket();
+ tokens.AppendIntegerTextToken(var, offset, size);
+ tokens.AppendCloseBracket();
+ return;
+ }
+
+ default: break;
+ }
+}
+
+
+void PseudoCFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement)
+{
+ GetExprTextInternal(instr, tokens, settings, asFullAst, precedence, statement);
+}
+
+
+void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement,
+ optional<bool> signedHint)
+{
+ // The lambdas in this function are here to reduce stack frame size of this function. Without them,
+ // complex expression can cause the process to crash from a stack overflow.
+ auto exprGuard = tokens.SetCurrentExpr(instr);
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform()))
+ {
+ tokens.Append(token);
+ }
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, " ");
+ }
+ if (settings && settings->IsOptionSet(ShowILOpcodes))
+ {
+ tokens.Append(OperationToken, "/*");
+ switch (instr.operation)
+ {
+ case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break;
+ case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break;
+ case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break;
+ case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break;
+ case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break;
+ case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break;
+ case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break;
+ case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break;
+ case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break;
+ case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break;
+ case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break;
+ case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break;
+ case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break;
+ case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break;
+ case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break;
+ case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break;
+ case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break;
+ case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break;
+ case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break;
+ case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break;
+ case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break;
+ case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break;
+ case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break;
+ case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break;
+ case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break;
+ case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break;
+ case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break;
+ case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break;
+ case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break;
+ case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break;
+ case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break;
+ case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break;
+ case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break;
+ case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break;
+ case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break;
+ case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break;
+ case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break;
+ case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break;
+ case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break;
+ case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break;
+ case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break;
+ case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break;
+ case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break;
+ case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break;
+ case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break;
+ case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break;
+ case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break;
+ case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break;
+ case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break;
+ case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break;
+ case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break;
+ case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break;
+ case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break;
+ case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break;
+ case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break;
+ case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break;
+ case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break;
+ case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break;
+ case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break;
+ case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break;
+ case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break;
+ case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break;
+ case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break;
+ case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break;
+ case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break;
+ case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break;
+ case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break;
+ case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break;
+ case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break;
+ case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break;
+ case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break;
+ case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break;
+ case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break;
+ case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break;
+ case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break;
+ case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break;
+ case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break;
+ case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break;
+ case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break;
+ case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break;
+ case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break;
+ case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break;
+ case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break;
+ case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break;
+ case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break;
+ case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break;
+ case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break;
+ case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break;
+ case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break;
+ case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break;
+ case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break;
+ case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break;
+ case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break;
+ case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break;
+ case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break;
+ case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break;
+ case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break;
+ case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break;
+ case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break;
+ case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break;
+ case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break;
+ case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break;
+ case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break;
+ case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break;
+ case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break;
+ case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break;
+ case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break;
+ case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break;
+ case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break;
+ case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break;
+ case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break;
+ case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break;
+ case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break;
+ case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break;
+ case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break;
+ case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break;
+ case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break;
+ case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break;
+ case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break;
+ case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break;
+ case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break;
+ case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break;
+ }
+ tokens.Append(OperationToken, "*/");
+ tokens.Append(TextToken, " ");
+ }
+
+ switch (instr.operation)
+ {
+ case HLIL_BLOCK:
+ [&]() {
+ const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>();
+ bool needSeparator = false;
+ for (auto i = exprs.begin(); i != exprs.end(); ++i)
+ {
+ // Don't show void returns at the very end of the function when printing
+ // the root of an AST, as it is implicit and almost always omitted in
+ // normal source code.
+ auto next = i;
+ ++next;
+ if (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) &&
+ (next == exprs.end()) && ((*i).operation == HLIL_RET) &&
+ ((*i).GetSourceExprs<HLIL_RET>().size() == 0))
+ continue;
+
+ // If the statement is one that contains additional blocks of code, insert a scope separator
+ // to visually separate the logic.
+ bool hasBlocks = false;
+ switch ((*i).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:
+ hasBlocks = true;
+ break;
+ default:
+ hasBlocks = false;
+ break;
+ }
+ if (needSeparator || (i != exprs.begin() && hasBlocks))
+ {
+ tokens.ScopeSeparator();
+ }
+ needSeparator = hasBlocks;
+
+ // Emit the lines for the statement itself
+ GetExprTextInternal(*i, tokens, settings, true, TopLevelOperatorPrecedence, true);
+ tokens.NewLine();
+ }
+ }();
+ break;
+
+ case HLIL_FOR:
+ [&]() {
+ const auto initExpr = instr.GetInitExpr<HLIL_FOR>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_FOR>();
+ const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>();
+
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "for ");
+ tokens.AppendOpenParen();
+ if (initExpr.operation != HLIL_NOP)
+ GetExprTextInternal(initExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (condExpr.operation != HLIL_NOP)
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ 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();
+ }
+ else
+ {
+ tokens.Append(KeywordToken, "while ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings);
+ tokens.AppendCloseParen();
+ }
+ }();
+ 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>();
+
+ tokens.Append(KeywordToken, "if ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ if (!asFullAst)
+ return;
+
+ 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)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else ");
+ GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ }
+ else if (falseExpr.operation != HLIL_NOP)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else");
+ scopeType = HighLevelILFunction::GetExprScopeType(falseExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.FinalizeScope();
+ }
+ }();
+ break;
+
+ case HLIL_WHILE:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>();
+
+ tokens.Append(KeywordToken, "while ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings);
+ tokens.AppendCloseParen();
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_DO_WHILE:
+ [&]() {
+ const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>();
+ 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();
+ }
+ else
+ {
+ tokens.Append(TextToken, "/* do */ ");
+ tokens.Append(KeywordToken, "while ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings);
+ tokens.AppendCloseParen();
+ }
+ }();
+ break;
+
+ case HLIL_SWITCH:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_SWITCH>();
+ const auto caseExprs = instr.GetCases<HLIL_SWITCH>();
+ const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>();
+
+ tokens.Append(KeywordToken, "switch ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(condExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ tokens.BeginScope(SwitchScopeType);
+ if (!asFullAst)
+ return;
+
+ for (const auto caseExpr : caseExprs)
+ {
+ GetExprTextInternal(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.NewLine();
+ }
+
+ if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE)
+ {
+ tokens.Append(KeywordToken, "default");
+ tokens.Append(TextToken, ":");
+ tokens.BeginScope(CaseScopeType);
+ GetExprTextInternal(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true);
+ tokens.EndScope(CaseScopeType);
+ tokens.FinalizeScope();
+ }
+
+ tokens.EndScope(SwitchScopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_CASE:
+ [&]() {
+ const auto valueExprs = instr.GetValueExprs<HLIL_CASE>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>();
+
+ for (size_t index{}; index < valueExprs.size(); index++)
+ {
+ const auto& valueExpr = valueExprs[index];
+ tokens.Append(KeywordToken, "case ");
+ GetExprTextInternal(valueExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, ":");
+ if (index != valueExprs.size() - 1)
+ tokens.NewLine();
+ }
+
+ if (!asFullAst)
+ return;
+
+ 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};
+
+ // 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 (std::find(operations.begin(), operations.end(), expr.operation) != operations.end())
+ {
+ disallowedOperation = true;
+ break;
+ }
+ }
+
+ if (!disallowedOperation)
+ {
+ tokens.NewLine();
+ 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();
+ }();
+ break;
+
+ case HLIL_BREAK:
+ [&]() {
+ tokens.Append(KeywordToken, "break");
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONTINUE:
+ [&]() {
+ tokens.Append(KeywordToken, "continue");
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ZX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ZX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, false);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_SX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, true, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, true);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_CALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>();
+
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+
+ vector<FunctionParameter> namedParams;
+ Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType();
+ if (functionType && (functionType->GetClass() == PointerTypeClass)
+ && (functionType->GetChildType()->GetClass() == FunctionTypeClass))
+ namedParams = functionType->GetChildType()->GetParameters();
+
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+
+ // If the type of the parameter is known to be a pointer to a string, then we directly render it as a
+ // string, regardless of its length
+ bool renderedAsString = false;
+ if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR)
+ {
+ auto exprType = namedParams[index].type;
+ if (exprType && (exprType->GetClass() == PointerTypeClass))
+ {
+ if (auto child = exprType->GetChildType(); child)
+ {
+ if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1)
+ || child->IsWideChar())
+ {
+ tokens.AppendPointerTextToken(parameterExprs[index],
+ parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
+ precedence, true);
+ renderedAsString = true;
+ }
+ }
+ }
+ }
+
+ if (!renderedAsString)
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_IMPORT:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
+ const auto symbolType = symbol->GetType();
+
+ if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
+ {
+ symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant);
+ const auto symbolShortName = symbol->GetShortName();
+ tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand);
+ return;
+ }
+
+ tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ARRAY_INDEX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ARRAY_INDEX>();
+ const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>();
+
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenBracket();
+ GetExprTextInternal(indexExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseBracket();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_INIT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>();
+ const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ const auto prevTypeTokens =
+ variableType ?
+ variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ variableType->GetTokensAfterName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (const auto ssaForm = instr.GetSSAForm(); ssaForm.operation == HLIL_VAR_INIT_SSA)
+ {
+ const auto ssaDest = ssaForm.GetDestSSAVariable<HLIL_VAR_INIT_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest);
+ }
+
+ // If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ if (appearsDead)
+ tokens.BeginForceZeroConfidence();
+
+ if (variableType)
+ {
+ for (auto typeToken: prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = destExpr.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ tokens.Append(TextToken, " ");
+ }
+ tokens.AppendVarTextToken(destExpr, instr, instr.size);
+ if (variableType)
+ {
+ for (auto typeToken: postTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = destExpr.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.Append(OperationToken, " = ");
+
+ // For the right side of the assignment, only use zero confidence if the instruction does
+ // not have any side effects
+ if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr))
+ {
+ tokens.EndForceZeroConfidence();
+ appearsDead = false;
+ }
+
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_DECLARE:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ const auto prevTypeTokens =
+ variableType ?
+ variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ variableType->GetTokensAfterName(platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ if (variableType)
+ {
+ for (auto typeToken: prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = variable.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ tokens.Append(TextToken, " ");
+ }
+ tokens.AppendVarTextToken(variable, instr, instr.size);
+ if (variableType)
+ {
+ for (auto typeToken: postTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = variable.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONST:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_FLOAT_CONST>();
+ if (instr.size == 4)
+ {
+ char valueStr[64];
+ union
+ {
+ float f;
+ uint32_t i;
+ } bits{};
+ bits.i = constant;
+ snprintf(valueStr, sizeof(valueStr), "%.9gf", bits.f);
+ tokens.Append(FloatingPointToken, InstructionAddressTokenContext, valueStr, instr.address);
+ }
+ else if (instr.size == 8)
+ {
+ char valueStr[64];
+ union
+ {
+ double f;
+ uint64_t i;
+ } bits{};
+ bits.i = constant;
+ snprintf(valueStr, sizeof(valueStr), "%.17g", bits.f);
+ string s = valueStr;
+ if ((s.find('.') == string::npos) && (s.find('e') == string::npos))
+ s += ".0";
+ tokens.Append(FloatingPointToken, InstructionAddressTokenContext, s, instr.address);
+ }
+ else
+ {
+ tokens.AppendIntegerTextToken(instr, constant, 8);
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST:
+ [&]() {
+ tokens.AppendConstantTextToken(instr, instr.GetConstant<HLIL_CONST>(), instr.size, settings, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_DATA:
+ [&]() {
+ // Constant data should be rendered according to the type of builtin function being used.
+ const ConstantData& data = instr.GetConstantData<HLIL_CONST_DATA>();
+ if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength())
+ {
+ switch (builtin)
+ {
+ case BuiltinStrcpy:
+ case BuiltinStrncpy:
+ {
+ string result(db.ToEscapedString(true));
+ tokens.Append(BraceToken, "\"");
+ tokens.Append(StringToken, ConstStringDataTokenContext, result, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ break;
+ }
+ case BuiltinMemset:
+ {
+ char buf[32];
+ if (data.value < 10)
+ snprintf(buf, sizeof(buf), "%" PRId64 "", data.value);
+ else
+ snprintf(buf, sizeof(buf), "0x%" PRIx64 "", data.value);
+
+ tokens.Append(BraceToken, "{");
+ tokens.Append(StringToken, ConstDataTokenContext, string(buf), instr.address, data.value);
+ tokens.Append(BraceToken, "}");
+ break;
+ }
+ default:
+ {
+ if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); unicode.has_value())
+ {
+ auto wideStringPrefix = (builtin == BuiltinWcscpy) ? "L" : "";
+ auto tokenContext = (builtin == BuiltinWcscpy) ? ConstStringDataTokenContext : ConstDataTokenContext;
+ tokens.Append(BraceToken, wideStringPrefix + string("\""));
+ tokens.Append(StringToken, tokenContext, unicode.value().first, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ }
+ else
+ {
+ string result(db.ToEscapedString(false, true));
+
+ tokens.Append(BraceToken, "\"");
+ tokens.Append(StringToken, ConstDataTokenContext, result, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ // TODO controls for emitting an initializer list?
+ // char str[32];
+ // string result;
+ // const uint8_t* bytes = (const uint8_t*)db.GetData();
+ // for (size_t i = 0; i < db.GetLength(); i++)
+ // {
+ // snprintf(str, sizeof(str), "0x%" PRIx8 ", ", bytes[i]);
+ // result += str;
+ // }
+ // if (result.size() > 2)
+ // result.erase(result.end() - 2);
+ // tokens.Append(StringToken, StringDisplayTokenContext, string("{ ") + result + string(" }"), instr.address, bytes.value);
+ }
+ break;
+ }
+ }
+ }
+ else
+ tokens.Append(StringToken, StringDisplayTokenContext, string("<invalid constant data>"), instr.address, data.value);
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_PTR:
+ [&]() {
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR>();
+ const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable);
+ tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName,
+ instr.exprIndex, variable.ToIdentifier(), instr.size);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_ASSIGN>();
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>();
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (destExpr.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaForm);
+ }
+ else if (destExpr.operation == HLIL_VAR)
+ {
+ if (const auto ssaForm = destExpr.GetSSAForm(); ssaForm.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaDest = ssaForm.GetSSAVariable<HLIL_VAR_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest);
+ }
+ }
+
+ // If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ if (appearsDead)
+ tokens.BeginForceZeroConfidence();
+
+ std::optional<string> assignUpdateOperator;
+ std::optional<HighLevelILInstruction> assignUpdateSource;
+ bool assignUpdateNegate = false;
+ const auto isSplit = destExpr.operation == HLIL_SPLIT;
+ std::optional<bool> assignSignedHint;
+ if (isSplit)
+ {
+ const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ GetExprTextInternal(high, tokens, settings, asFullAst, precedence);
+ tokens.Append(OperationToken, " = ");
+ tokens.Append(OperationToken, "HIGH");
+ AppendSingleSizeToken(high.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ tokens.AppendCloseParen();
+ tokens.AppendSemicolon();
+ tokens.NewLine();
+ GetExprTextInternal(low, tokens, settings, asFullAst, precedence);
+ }
+ else
+ {
+ // Check for assignment with an operator on the same variable as the destination
+ // (for example, `a = a + 2` should be shown as `a += 2`)
+ if ((srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_SUB || srcExpr.operation == HLIL_MUL
+ || srcExpr.operation == HLIL_DIVU || srcExpr.operation == HLIL_DIVS
+ || srcExpr.operation == HLIL_LSL || srcExpr.operation == HLIL_LSR
+ || srcExpr.operation == HLIL_ASR || (instr.size != 0 && srcExpr.operation == HLIL_AND)
+ || (instr.size != 0 && srcExpr.operation == HLIL_OR)
+ || (instr.size != 0 && srcExpr.operation == HLIL_XOR))
+ && (srcExpr.GetLeftExpr() == destExpr))
+ {
+ auto lessThanZero = [](uint64_t value, uint64_t width) -> bool {
+ return ((1UL << ((width * 8) - 1UL)) & value) != 0;
+ };
+ switch (srcExpr.operation)
+ {
+ case HLIL_ADD:
+ assignUpdateOperator = " += ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+
+ if ((assignUpdateSource.value().operation == HLIL_CONST)
+ && lessThanZero(
+ assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size)
+ && assignUpdateSource.value().size >= instr.size)
+ {
+ // Convert addition of a negative constant into subtraction of a positive constant
+ assignUpdateOperator = " -= ";
+ assignUpdateNegate = true;
+ }
+ break;
+ case HLIL_SUB:
+ assignUpdateOperator = " -= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_MUL:
+ assignUpdateOperator = " *= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_DIVU:
+ assignUpdateOperator = " u/= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ assignSignedHint = false;
+ break;
+ case HLIL_DIVS:
+ assignUpdateOperator = " s/= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ assignSignedHint = true;
+ break;
+ case HLIL_LSL:
+ assignUpdateOperator = " <<= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_LSR:
+ assignUpdateOperator = " u>>= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_ASR:
+ assignUpdateOperator = " s>>= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_AND:
+ assignUpdateOperator = " &= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_OR:
+ assignUpdateOperator = " |= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_XOR:
+ assignUpdateOperator = " ^= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (
+ (srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_MUL
+ || (instr.size != 0 && srcExpr.operation == HLIL_AND)
+ || (instr.size != 0 && srcExpr.operation == HLIL_OR)
+ || (instr.size != 0 && srcExpr.operation == HLIL_XOR))
+ && (srcExpr.GetRightExpr() == destExpr))
+ {
+ switch (srcExpr.operation)
+ {
+ case HLIL_ADD:
+ assignUpdateOperator = " += ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_MUL:
+ assignUpdateOperator = " *= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_AND:
+ assignUpdateOperator = " &= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_OR:
+ assignUpdateOperator = " |= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_XOR:
+ assignUpdateOperator = " ^= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, precedence);
+ if (assignUpdateOperator.has_value() && assignUpdateSource.has_value())
+ tokens.Append(OperationToken, assignUpdateOperator.value());
+ else
+ tokens.Append(OperationToken, " = ");
+
+ // For the right side of the assignment, only use zero confidence if the instruction does
+ // not have any side effects
+ if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr))
+ {
+ tokens.EndForceZeroConfidence();
+ appearsDead = false;
+ }
+
+ if (isSplit)
+ {
+// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ tokens.Append(OperationToken, "LOW");
+ AppendSingleSizeToken(low.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ }
+
+ if (assignUpdateOperator.has_value() && assignUpdateSource.has_value())
+ {
+ if (assignUpdateNegate)
+ {
+ tokens.AppendIntegerTextToken(assignUpdateSource.value(),
+ -BNSignExtend(
+ assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size, 8),
+ assignUpdateSource.value().size);
+ }
+ else
+ {
+ GetExprTextInternal(assignUpdateSource.value(), tokens, settings, asFullAst,
+ AssignmentOperatorPrecedence, false, assignSignedHint);
+ }
+ }
+ else
+ {
+ GetExprTextInternal(
+ srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, false, assignSignedHint);
+ }
+
+ if (isSplit)
+ tokens.AppendCloseParen();
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN_UNPACK:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>();
+ const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>();
+ const auto firstExpr = destExprs[0];
+
+ GetExprTextInternal(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ tokens.Append(OperationToken, " = ");
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_STRUCT_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>();
+ const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>();
+
+ const auto type = GetFieldType(srcExpr, false);
+ const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false);
+ if (fieldDisplayType == FieldDisplayOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ tokens.AppendOpenParen();
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, "char");
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+
+ tokens.Append(OperationToken, " + ");
+ tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size);
+ tokens.AppendCloseParen();
+
+ char offsetStr[64];
+ snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset);
+
+ vector<string> nameList { offsetStr };
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+ }
+ else if (fieldDisplayType == FieldDisplayMemberOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, "char");
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendCloseParen();
+ }
+ /* rest is rendered in AppendFieldTextTokens */
+ }
+ else
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ }
+
+ AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF:
+ [&]() {
+ auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>();
+
+ auto appendMaybeBrace = [&](const InstructionTextToken& token)
+ {
+ if (token.type == BraceToken)
+ {
+ if (token.text == "(")
+ tokens.AppendOpenParen();
+ else if (token.text == ")")
+ tokens.AppendCloseParen();
+ else if (token.text == "[")
+ tokens.AppendOpenBracket();
+ else if (token.text == "]")
+ tokens.AppendCloseBracket();
+ else if (token.text == "{")
+ tokens.AppendOpenBrace();
+ else if (token.text == "}")
+ tokens.AppendCloseBrace();
+ else
+ tokens.Append(token);
+ }
+ else
+ {
+ tokens.Append(token);
+ }
+ };
+
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+
+ vector<InstructionTextToken> pointerTokens{};
+ if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, precedence) == DataSymbolResult)
+ {
+ const auto type = srcExpr.GetType();
+ if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth())
+ {
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ }
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, false, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendCloseParen();
+ }
+ }
+ else
+ {
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+ }
+ }
+ else
+ {
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+ }
+ }
+ else
+ {
+ size_t derefCount{};
+ while (srcExpr.operation == HLIL_DEREF)
+ {
+ auto next = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ if (next.size == srcExpr.size)
+ {
+ srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ derefCount++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ for (size_t index{}; index <= derefCount; index++)
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+
+ AppendSizeToken(instr.size, false, tokens);
+
+ for (size_t index{}; index <= derefCount; index++)
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_TAILCALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_TAILCALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>();
+
+ tokens.Append(AnnotationToken, "/* tailcall */");
+ tokens.NewLine();
+ tokens.Append(KeywordToken, "return ");
+ GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+ GetExprTextInternal(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADDRESS_OF:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "&");
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_E:
+ [&]() {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CMP_E:
+ [&]() {
+ if ((instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST
+ || instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST_PTR)
+ && instr.GetRightExpr<HLIL_CMP_E>().GetConstant() == 0)
+ {
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "!");
+ GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_E>(), tokens, settings, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+ else
+ {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_NE:
+ [&]() {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CMP_NE:
+ [&]() {
+ if ((instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST
+ || instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST_PTR)
+ && instr.GetRightExpr<HLIL_CMP_NE>().GetConstant() == 0)
+ {
+ GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_NE>(), tokens, settings, precedence);
+ }
+ else
+ {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_LT:
+ case HLIL_CMP_SLT:
+ case HLIL_CMP_ULT:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLT)
+ cmpSigned = true;
+ AppendComparison(" < ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_LE:
+ case HLIL_CMP_SLE:
+ case HLIL_CMP_ULE:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLE)
+ cmpSigned = true;
+ AppendComparison(" <= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_GE:
+ case HLIL_CMP_SGE:
+ case HLIL_CMP_UGE:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGE)
+ cmpSigned = true;
+ AppendComparison(" >= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_GT:
+ case HLIL_CMP_SGT:
+ case HLIL_CMP_UGT:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGT)
+ cmpSigned = true;
+ AppendComparison(" > ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_AND:
+ [&]() {
+ bool parens = instr.size == 0 ?
+ precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalOrOperatorPrecedence :
+ precedence >= EqualityOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(instr.size == 0 ? " && " : " & ", instr, tokens, settings, asFullAst,
+ instr.size == 0 ? LogicalAndOperatorPrecedence : BitwiseAndOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_OR:
+ [&]() {
+ bool parens = (instr.size == 0) ?
+ precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalAndOperatorPrecedence :
+ precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(instr.size == 0 ? " || " : " | ", instr, tokens, settings, asFullAst,
+ instr.size == 0 ? LogicalOrOperatorPrecedence : BitwiseOrOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_XOR:
+ [&]() {
+ bool parens = precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence ||
+ precedence == BitwiseOrOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" ^ ", instr, tokens, settings, asFullAst, BitwiseXorOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADC:
+ case HLIL_ADD_OVERFLOW:
+ case HLIL_FADD:
+ case HLIL_ADD:
+ [&]() {
+ bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" + ", instr, tokens, settings, asFullAst, AddOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SUB:
+ [&]{
+ // Check for offset pointers
+ auto left = instr.GetLeftExpr<HLIL_SUB>();
+ auto right = instr.GetRightExpr<HLIL_SUB>();
+ if (left.operation == HLIL_VAR && right.operation == HLIL_CONST)
+ {
+ auto var = left.GetVariable<HLIL_VAR>();
+ auto srcOffset = right.GetConstant<HLIL_CONST>();
+ auto varType = GetFunction()->GetVariableType(var);
+ if (varType
+ && varType->GetClass() == PointerTypeClass
+ && varType->GetNamedTypeReference()
+ && varType->GetOffset() == srcOffset)
+ {
+ // Yes
+ tokens.Append(OperationToken, "ADJ");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(left, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendCloseParen();
+ return;
+ }
+ }
+
+ // No
+ bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+ case HLIL_SBB:
+ case HLIL_FSUB:
+ [&]() {
+ bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LSL:
+ [&]() {
+ bool parens = precedence > ShiftOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" << ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LSR:
+ case HLIL_ASR:
+ [&]() {
+ bool parens = precedence > ShiftOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" >> ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_FMUL:
+ case HLIL_MUL:
+ case HLIL_MULU_DP:
+ case HLIL_MULS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> mulSigned;
+ if (instr.operation == HLIL_MULU_DP)
+ mulSigned = false;
+ else if (instr.operation == HLIL_MULS_DP)
+ mulSigned = true;
+ AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, MultiplyOperatorPrecedence, mulSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FDIV:
+ case HLIL_DIVU:
+ case HLIL_DIVU_DP:
+ case HLIL_DIVS:
+ case HLIL_DIVS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence
+ || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence
+ || precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> divSigned;
+ if (instr.operation == HLIL_DIVU || instr.operation == HLIL_DIVU_DP)
+ divSigned = false;
+ else if (instr.operation == HLIL_DIVS || instr.operation == HLIL_DIVS_DP)
+ divSigned = true;
+ AppendTwoOperand(" / ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, divSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_MODU:
+ case HLIL_MODU_DP:
+ case HLIL_MODS:
+ case HLIL_MODS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence
+ || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence
+ || precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> modSigned;
+ if (instr.operation == HLIL_MODU || instr.operation == HLIL_MODU_DP)
+ modSigned = false;
+ else if (instr.operation == HLIL_MODS || instr.operation == HLIL_MODS_DP)
+ modSigned = true;
+ AppendTwoOperand(" % ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, modSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_ROR:
+ [&]() {
+ AppendTwoOperandFunction("ROR", instr, tokens, settings);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ROL:
+ [&]() {
+ AppendTwoOperandFunction("ROL", instr, tokens, settings);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RLC:
+ [&]() {
+ AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RRC:
+ [&]() {
+ AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_TEST_BIT:
+ [&]() {
+ AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOOR:
+ [&]() {
+ tokens.Append(OperationToken, "floor");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(instr.GetSourceExpr<HLIL_FLOOR>(), tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CEIL:
+ [&]() {
+ tokens.Append(OperationToken, "ceil");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(instr.GetSourceExpr<HLIL_CEIL>(), tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FTRUNC:
+ [&]() {
+ auto src = instr.GetSourceExpr<HLIL_FTRUNC>();
+ string trunc;
+ if (src.size == 4)
+ trunc = "truncf";
+ else if (src.size == 8)
+ trunc = "trunc";
+ else if (src.size == 10)
+ trunc = "truncl";
+ else
+ trunc = "trunc" + std::to_string(src.size) + "f";
+ tokens.Append(OperationToken, trunc);
+ tokens.AppendOpenParen();
+ GetExprTextInternal(src, tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FABS:
+ [&]() {
+ auto src = instr.GetSourceExpr<HLIL_FABS>();
+ string fabs;
+ if (src.size == 4)
+ fabs = "fabsf";
+ else if (src.size == 8)
+ fabs = "fabs";
+ else if (src.size == 10)
+ fabs = "fabsl";
+ else
+ fabs = "fabs" + std::to_string(src.size) + "f";
+ tokens.Append(OperationToken, fabs);
+ tokens.AppendOpenParen();
+ GetExprTextInternal(src, tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FSQRT:
+ [&]() {
+ auto src = instr.GetSourceExpr<HLIL_FSQRT>();
+ string sqrt;
+ if (src.size == 4)
+ sqrt = "sqrtf";
+ else if (src.size == 8)
+ sqrt = "sqrt";
+ else if (src.size == 10)
+ sqrt = "sqrtl";
+ else
+ sqrt = "sqrt" + std::to_string(src.size) + "f";
+ tokens.Append(OperationToken, sqrt);
+ tokens.AppendOpenParen();
+ GetExprTextInternal(src, tokens, settings);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ROUND_TO_INT:
+ [&]() {
+ AppendTwoOperandFunction("round", instr, tokens, settings, false);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_O:
+ [&]() {
+ AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_UO:
+ [&]() {
+ AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_NOT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_NOT>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, instr.size == 0 ? "!" : "~");
+ GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FNEG:
+ case HLIL_NEG:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "-");
+ tokens.AppendOpenParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, true);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONV:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType =
+ instr.size == 2 ? "float16_t" :
+ instr.size == 4 ? "float" :
+ instr.size == 8 ? "double" :
+ instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t";
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, floatType.c_str());
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_TO_INT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, true, tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_BOOL_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_BOOL_TO_INT>();
+
+ bool parens = precedence > TernaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, TernaryOperatorPrecedence);
+ tokens.Append(OperationToken, " ? ");
+ tokens.AppendIntegerTextToken(instr, 1, 1);
+ tokens.Append(OperationToken, " : ");
+ tokens.AppendIntegerTextToken(instr, 0, 1);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_INT_TO_FLOAT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_INT_TO_FLOAT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType =
+ instr.size == 2 ? "float16_t" :
+ instr.size == 4 ? "float" :
+ instr.size == 8 ? "double" :
+ instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t";
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, floatType.c_str());
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_INTRINSIC:
+ [&]() {
+ const auto intrinsic = instr.GetIntrinsic<HLIL_INTRINSIC>();
+ const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic);
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>();
+
+ tokens.Append(KeywordToken, intrinsicName, intrinsic);
+ tokens.AppendOpenParen();
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+ GetExprTextInternal(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RET:
+ [&]() {
+ const auto srcExprs = instr.GetSourceExprs<HLIL_RET>();
+
+ tokens.Append(KeywordToken, "return");
+ for (size_t index{}; index < srcExprs.size(); index++)
+ {
+ const auto& srcExpr = srcExprs[index];
+ if (index == 0) tokens.Append(TextToken, " ");
+ if (index != 0) tokens.Append(TextToken, ", ");
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst);
+ }
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_NORET:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* no return */");
+ }();
+ break;
+
+ case HLIL_UNREACHABLE:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* unreachable */");
+ }();
+ break;
+
+ case HLIL_JUMP:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_JUMP>();
+ tokens.Append(AnnotationToken, "/* jump -> ");
+ GetExprTextInternal(destExpr, tokens, settings);
+ tokens.Append(AnnotationToken, " */");
+ }();
+ break;
+
+ case HLIL_UNDEF:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* undefined */");
+ }();
+ break;
+
+ case HLIL_TRAP:
+ [&]() {
+ const auto vector = instr.GetVector<HLIL_TRAP>();
+ tokens.Append(KeywordToken, "trap");
+ tokens.AppendOpenParen();
+ tokens.AppendIntegerTextToken(instr, vector, 8);
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_DEREF_FIELD>();
+ const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>();
+ auto type = srcExpr.GetType().GetValue();
+
+ if (type && (type->GetClass() == PointerTypeClass))
+ type = type->GetChildType().GetValue();
+
+ if (type && (type->GetClass() == NamedTypeReferenceClass))
+ type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference());
+
+ bool derefOffset = false;
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ bool outer = true;
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ BNSymbolDisplayResult symbolType;
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+ symbolType = tokens.AppendPointerTextToken(
+ srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprTextInternal(
+ srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ symbolType = OtherSymbolResult;
+ }
+
+ const auto displayDeref = symbolType != DataSymbolResult;
+ if (displayDeref && outer)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ outer = false;
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+
+ tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0,
+ BN_FULL_CONFIDENCE, nameList);
+ }),
+ memberIndexHint)
+ return;
+ }
+ else if (type && (type->GetClass() == StructureTypeClass))
+ {
+ derefOffset = true;
+ }
+
+ if (derefOffset || offset != 0)
+ {
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens);
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+ tokens.AppendOpenParen();
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ tokens.Append(TypeNameToken, "char");
+ tokens.Append(TextToken, "*");
+ tokens.AppendCloseParen();
+ }
+
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+ tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, true, AddOperatorPrecedence);
+ }
+
+ tokens.Append(OperationToken, " + ");
+ tokens.AppendIntegerTextToken(instr, offset, instr.size);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_EXTERN_PTR:
+ [&]() {
+ const int64_t val = instr.GetOffset<HLIL_EXTERN_PTR>();
+ if (val != 0)
+ tokens.AppendOpenParen();
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_EXTERN_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (val != 0)
+ {
+ char valStr[32];
+ if (val >= 0)
+ {
+ tokens.Append(OperationToken, " + ");
+ if (val <= 9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val);
+ }
+ else
+ {
+ tokens.Append(OperationToken, " - ");
+ if (val >= -9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, -val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, -val);
+ }
+ tokens.Append(IntegerToken, valStr, val);
+ tokens.AppendCloseParen();
+ }
+
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SYSCALL:
+ [&]() {
+ tokens.Append(KeywordToken, "syscall");
+ tokens.AppendOpenParen();
+ const auto operandList = instr.GetParameterExprs<HLIL_SYSCALL>();
+ vector<FunctionParameter> namedParams;
+ bool skipSyscallNumber = false;
+ if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST))
+ {
+ const auto platform = GetFunction()->GetPlatform();
+ if (platform)
+ {
+ const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>();
+ const auto syscallName = platform->GetSystemCallName(syscall);
+ if (settings && settings->GetCallParameterHints() != NeverShowParameterHints)
+ {
+ const auto functionType = platform->GetSystemCallType(syscall);
+ if (functionType && (functionType->GetClass() == FunctionTypeClass))
+ namedParams = functionType->GetParameters();
+ }
+ if (syscallName.length())
+ {
+ tokens.Append(TextToken, syscallName);
+ tokens.Append(TextToken, " ");
+ tokens.AppendOpenBrace();
+ GetExprTextInternal(operandList[0], tokens, settings);
+ tokens.AppendCloseBrace();
+ skipSyscallNumber = true;
+ }
+ }
+ }
+ for (size_t i = (skipSyscallNumber ? 1 : 0); i < operandList.size(); i++)
+ {
+ if (i != 0)
+ tokens.Append(TextToken, ", ");
+ GetExprTextInternal(operandList[i], tokens, settings);
+ }
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_BP:
+ [&]() {
+ tokens.Append(KeywordToken, "breakpoint");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_UNIMPL_MEM:
+ case HLIL_UNIMPL:
+ [&]() {
+ const auto hlilFunc = GetHighLevelILFunction();
+ const auto instructionText = hlilFunc->GetExprText(hlilFunc->GetInstruction(
+ hlilFunc->GetInstructionForExpr(instr.exprIndex)).exprIndex, true, settings);
+ tokens.Append(AnnotationToken, "/* ");
+ for (const auto& token : instructionText[0].tokens)
+ tokens.Append(token.type, token.text, token.value);
+
+ if (instructionText.size() > 1)
+ tokens.Append(AnnotationToken, "...");
+
+ tokens.Append(AnnotationToken, " */");
+ }();
+ break;
+
+ case HLIL_NOP:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* nop */");
+ }();
+ break;
+
+ case HLIL_GOTO:
+ [&]() {
+ const auto target = instr.GetTarget<HLIL_GOTO>();
+ tokens.Append(KeywordToken, "goto ");
+ tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target);
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LABEL:
+ [&]() {
+ const auto target = instr.GetTarget<HLIL_LABEL>();
+ tokens.DecreaseIndent();
+ tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target);
+ tokens.Append(TextToken, ":");
+ tokens.IncreaseIndent();
+ }();
+ break;
+
+ case HLIL_LOW_PART:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ AppendSizeToken(instr.size, signedHint.value_or(true), tokens);
+ tokens.AppendCloseParen();
+ GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (statement)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SPLIT: break;
+ default:
+ [&]() {
+ char buf[64]{};
+ snprintf(buf, sizeof(buf), "/* <UNIMPLEMENTED, %x> */", instr.operation);
+ tokens.Append(AnnotationToken, buf);
+ }();
+ break;
+ }
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendCloseParen();
+ }
+}
+
+
+string PseudoCFunction::GetAnnotationStartString() const
+{
+ // Show annotations as C-style inline comments
+ return "/* ";
+}
+
+
+string PseudoCFunction::GetAnnotationEndString() const
+{
+ // Show annotations as C-style inline comments
+ return " */";
+}
+
+
+PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C")
+{
+}
+
+
+Ref<LanguageRepresentationFunction> PseudoCFunctionType::Create(Architecture* arch, Function* owner,
+ HighLevelILFunction* highLevelILFunction)
+{
+ return new PseudoCFunction(arch, owner, highLevelILFunction);
+}
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+#ifndef DEMO_EDITION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ }
+#endif
+
+#ifdef DEMO_EDITION
+ bool PseudoCPluginInit()
+#else
+ BINARYNINJAPLUGIN bool CorePluginInit()
+#endif
+ {
+ LanguageRepresentationFunctionType* type = new PseudoCFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+ return true;
+ }
+}
diff --git a/lang/c/pseudoc.h b/lang/c/pseudoc.h
new file mode 100644
index 00000000..664e7695
--- /dev/null
+++ b/lang/c/pseudoc.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
+{
+ BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL;
+
+ enum FieldDisplayType
+ {
+ FieldDisplayName,
+ FieldDisplayOffset,
+ FieldDisplayMemberOffset,
+ FieldDisplayNone
+ };
+
+ BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref);
+ FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref);
+
+ BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence);
+ std::string GetSizeToken(size_t size, bool isSigned);
+ void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true);
+ void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref = true);
+ void GetExprTextInternal(const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false,
+ std::optional<bool> signedHint = std::nullopt);
+
+protected:
+ void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) override;
+ void BeginLines(
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ void EndLines(
+ const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+
+public:
+ PseudoCFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
+
+ std::string GetAnnotationStartString() const override;
+ std::string GetAnnotationEndString() const override;
+};
+
+class PseudoCFunctionType: public BinaryNinja::LanguageRepresentationFunctionType
+{
+public:
+ PseudoCFunctionType();
+ BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+};
diff --git a/lang/rust/CMakeLists.txt b/lang/rust/CMakeLists.txt
new file mode 100644
index 00000000..e374d8e9
--- /dev/null
+++ b/lang/rust/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(lang_pseudorust)
+
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.h)
+
+if(DEMO)
+ add_library(lang_pseudorust STATIC ${SOURCES})
+else()
+ add_library(lang_pseudorust SHARED ${SOURCES})
+endif()
+
+target_include_directories(lang_pseudorust
+ PRIVATE ${PROJECT_SOURCE_DIR})
+
+if(WIN32)
+ target_link_directories(lang_pseudorust
+ PRIVATE ${BN_INSTALL_DIR})
+ target_link_libraries(lang_pseudorust binaryninjaapi binaryninjacore)
+else()
+ target_link_libraries(lang_pseudorust binaryninjaapi)
+endif()
+
+set_target_properties(lang_pseudorust PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(lang_pseudorust)
+ set_target_properties(lang_pseudorust PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/lang/rust/pseudorust.cpp b/lang/rust/pseudorust.cpp
new file mode 100644
index 00000000..278bbaa8
--- /dev/null
+++ b/lang/rust/pseudorust.cpp
@@ -0,0 +1,2925 @@
+#include <inttypes.h>
+#include "pseudorust.h"
+#include "rusttypes.h"
+#include "highlevelilinstruction.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+PseudoRustFunction::PseudoRustFunction(
+ Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) :
+ LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction)
+{
+}
+
+
+void PseudoRustFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens)
+{
+ // Braces must always be turned on for Rust
+ tokens.SetBraceRequirement(BracesAlwaysRequired);
+
+ // Multiple statements in a `match` require braces around them
+ tokens.SetBracesAroundSwitchCases(true);
+
+ // If the user hasn't specified a preference on brace placement, use the Rust standard style
+ tokens.SetDefaultBracesOnSameLine(true);
+
+ // Rust doesn't allow omitting the braces around conditional bodies
+ tokens.SetSimpleScopeAllowed(false);
+}
+
+
+void PseudoRustFunction::BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens)
+{
+ if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex)
+ {
+ // At top level, add braces around the entire function
+ tokens.AppendOpenBrace();
+ tokens.NewLine();
+ tokens.IncreaseIndent();
+ }
+}
+
+
+void PseudoRustFunction::EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens)
+{
+ if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex)
+ {
+ // At top level, add braces around the entire function
+ tokens.NewLine();
+ tokens.DecreaseIndent();
+ tokens.AppendCloseBrace();
+ }
+}
+
+
+BNSymbolDisplayResult PseudoRustFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val,
+ vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence)
+{
+ Confidence<Ref<Type>> type = instr.GetType();
+ if (type && (type->GetClass() == PointerTypeClass) && type->IsConst())
+ {
+ string stringValue;
+ size_t childWidth = 0;
+ if (auto child = type->GetChildType(); child)
+ childWidth = child->GetWidth();
+ if (auto strType = GetFunction()->GetView()->CheckForStringAnnotationType(val, stringValue, false, false, childWidth); strType.has_value())
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(BraceToken, DisassemblyTextRenderer::GetStringLiteralPrefix(strType.value()) + string("\""));
+ tokens.emplace_back(StringToken, StringReferenceTokenContext, stringValue, instr.address, strType.value());
+ tokens.emplace_back(BraceToken, "\"");
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+ }
+
+ if (GetFunction())
+ {
+ // If the pointer has a value of 0, check if it points to a valid address by
+ // 1. If the binary is relocatable, assign the pointer as nullptr
+ // 2. else, check if the constant zero which being referenced is a pointer(display as symbol) or not(display as nullptr)
+ if(val == 0x0 && type && (type->GetClass() == PointerTypeClass))
+ {
+ if (GetFunction()->GetView()->IsRelocatable())
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val);
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+
+ auto arch = GetHighLevelILFunction()->GetArchitecture();
+ auto refs = GetHighLevelILFunction()->GetFunction()->GetConstantsReferencedByInstructionIfAvailable(
+ arch, instr.address);
+ bool constantZeroBeingReferencedIsPointer = false;
+
+ for (const BNConstantReference& ref : refs)
+ if (ref.value == 0x0 && ref.pointer)
+ constantZeroBeingReferencedIsPointer = true;
+ if (!constantZeroBeingReferencedIsPointer)
+ {
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(OperationToken, "*");
+ }
+ tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val);
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+ }
+ }
+
+ Ref<BinaryView> data = GetFunction()->GetView();
+ vector<InstructionTextToken> symTokens;
+ BNSymbolDisplayResult result = DisassemblyTextRenderer::AddSymbolTokenStatic(symTokens, val, 0,
+ BN_INVALID_OPERAND, data, settings ? settings->GetMaximumSymbolWidth() : 0, GetFunction(),
+ BN_FULL_CONFIDENCE, symbolDisplay, precedence, instr.address);
+ if (result != NoSymbolAvailable)
+ {
+ for (auto& i : symTokens)
+ tokens.emplace_back(i);
+ return result;
+ }
+ }
+
+ if (symbolDisplay == DereferenceNonDataSymbols)
+ {
+ if (precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, "(");
+
+ tokens.emplace_back(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.emplace_back(BraceToken, "(");
+ tokens.emplace_back(TypeNameToken, GetSizeToken(instr.size, false));
+ tokens.emplace_back(OperationToken, "*");
+ tokens.emplace_back(BraceToken, ")");
+ }
+ }
+
+ char valStr[32];
+ if (val >= 0)
+ {
+ if (val <= 9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val);
+ }
+ else
+ {
+ if (val >= -9)
+ snprintf(valStr, sizeof(valStr), "-%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "-0x%" PRIx64, val);
+ }
+
+ tokens.emplace_back(PossibleAddressToken, InstructionAddressTokenContext, valStr, instr.address, val);
+
+ if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence)
+ tokens.emplace_back(BraceToken, ")");
+ return OtherSymbolResult;
+}
+
+
+string PseudoRustFunction::GetSizeToken(size_t size, bool isSigned)
+{
+ char sizeStr[32];
+
+ switch (size)
+ {
+ case 0:
+ return {};
+ case 1:
+ return (isSigned ? "i8" : "u8");
+ case 2:
+ return (isSigned ? "i16" : "u16");
+ case 4:
+ return (isSigned ? "i32" : "u32");
+ case 8:
+ return (isSigned ? "i64" : "u64");
+ case 10:
+ return (isSigned ? "i80" : "u80");
+ case 16:
+ return (isSigned ? "i128" : "u128");
+ }
+
+ snprintf(sizeStr, sizeof(sizeStr), "%s%" PRIuPTR, isSigned ? "i" : "u", size);
+ return {sizeStr};
+}
+
+
+void PseudoRustFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter)
+{
+ const auto token = GetSizeToken(size, isSigned);
+ if (!token.empty())
+ emitter.Append(TypeNameToken, token);
+}
+
+
+void PseudoRustFunction::AppendSingleSizeToken(
+ size_t size, BNInstructionTextTokenType type, HighLevelILTokenEmitter& emitter)
+{
+ char sizeStr[32];
+
+ switch (size)
+ {
+ case 0:
+ break;
+ case 1:
+ emitter.Append(type, "B");
+ break;
+ case 2:
+ emitter.Append(type, "W");
+ break;
+ case 4:
+ emitter.Append(type, "D");
+ break;
+ case 8:
+ emitter.Append(type, "Q");
+ break;
+ case 10:
+ emitter.Append(type, "T");
+ break;
+ case 16:
+ emitter.Append(type, "O");
+ break;
+ default:
+ snprintf(sizeStr, sizeof(sizeStr), "%" PRIuPTR "", size);
+ emitter.Append(type, sizeStr);
+ break;
+ }
+}
+
+
+void PseudoRustFunction::AppendComparison(const string& comparison, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+
+ GetExprText(leftExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint);
+ emitter.Append(OperationToken, comparison);
+ GetExprText(rightExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint);
+}
+
+
+void PseudoRustFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr,
+ HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence,
+ std::optional<bool> signedHint)
+{
+ const auto& twoOperand = instr.AsTwoOperand();
+ const auto leftExpr = twoOperand.GetLeftExpr();
+ const auto rightExpr = twoOperand.GetRightExpr();
+ BNOperatorPrecedence leftPrecedence = precedence;
+ switch (precedence)
+ {
+ case SubOperatorPrecedence:
+ // Treat left side of subtraction as same level as addition. This lets
+ // (a - b) - c be represented as a - b - c, but a - (b - c) does not
+ // simplify at rendering
+ leftPrecedence = AddOperatorPrecedence;
+ break;
+ case DivideOperatorPrecedence:
+ // Treat left side of divison as same level as multiplication. This lets
+ // (a / b) / c be represented as a / b / c, but a / (b / c) does not
+ // simplify at rendering
+ leftPrecedence = MultiplyOperatorPrecedence;
+ break;
+ default:
+ break;
+ }
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ emitter.Append(OperationToken, "COMBINE");
+ emitter.AppendOpenParen();
+ GetExprText(high, emitter, settings, asFullAst);
+ emitter.Append(TextToken, ", ");
+ GetExprText(low, emitter, settings, asFullAst);
+ emitter.AppendCloseParen();
+ }
+
+ if (operand == " + " || operand == " - ")
+ {
+ const auto exprType = leftExpr.GetType();
+ if (exprType && exprType->IsPointer())
+ {
+ GetExprText(leftExpr, emitter, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ emitter.Append(TextToken, ".");
+ emitter.Append(OperationToken, "byte_offset");
+ emitter.AppendOpenParen();
+ if (operand == " - ")
+ {
+ emitter.Append(OperationToken, "-");
+ GetExprText(rightExpr, emitter, settings, asFullAst, UnaryOperatorPrecedence);
+ }
+ else
+ {
+ GetExprText(rightExpr, emitter, settings, asFullAst);
+ }
+ emitter.AppendCloseParen();
+ return;
+ }
+ }
+
+ GetExprText(leftExpr, emitter, settings, asFullAst, leftPrecedence, InnerExpression, signedHint);
+
+ auto lessThanZero = [](uint64_t value, uint64_t width) -> bool {
+ return ((1UL << ((width * 8) - 1UL)) & value) != 0;
+ };
+
+ if ((operand == " + ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) &&
+ rightExpr.size >= leftExpr.size)
+ {
+ // Convert addition of a negative constant into subtraction of a positive constant
+ emitter.Append(OperationToken, " - ");
+ emitter.AppendIntegerTextToken(
+ rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+ if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) &&
+ rightExpr.size >= leftExpr.size)
+ {
+ // Convert subtraction of a negative constant into addition of a positive constant
+ emitter.Append(OperationToken, " + ");
+ emitter.AppendIntegerTextToken(
+ rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size);
+ return;
+ }
+
+ emitter.Append(OperationToken, operand);
+ GetExprText(rightExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint);
+}
+
+
+void PseudoRustFunction::AppendTwoOperandFunction(const string& function,
+ const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings,
+ bool sizeToken)
+{
+ const auto& twoOperand = instr.AsTwoOperand();
+ const auto leftExpr = twoOperand.GetLeftExpr();
+ const auto rightExpr = twoOperand.GetRightExpr();
+
+ emitter.Append(OperationToken, function);
+ if (sizeToken)
+ AppendSingleSizeToken(twoOperand.size, OperationToken, emitter);
+ emitter.AppendOpenParen();
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ emitter.Append(OperationToken, "COMBINE");
+ emitter.AppendOpenParen();
+ GetExprText(high, emitter, settings);
+ emitter.Append(TextToken, ", ");
+ GetExprText(low, emitter, settings);
+ emitter.AppendCloseParen();
+ }
+
+ GetExprText(leftExpr, emitter, settings);
+ emitter.Append(TextToken, ", ");
+ GetExprText(rightExpr, emitter, settings);
+
+ emitter.AppendCloseParen();
+}
+
+
+void PseudoRustFunction::AppendTwoOperandMethodCall(const string& function,
+ const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings)
+{
+ const auto& twoOperand = instr.AsTwoOperand();
+ const auto leftExpr = twoOperand.GetLeftExpr();
+ const auto rightExpr = twoOperand.GetRightExpr();
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ emitter.Append(OperationToken, "COMBINE");
+ emitter.AppendOpenParen();
+ GetExprText(high, emitter, settings);
+ emitter.Append(TextToken, ", ");
+ GetExprText(low, emitter, settings);
+ emitter.AppendCloseParen();
+ }
+ else
+ {
+ GetExprText(leftExpr, emitter, settings, MemberAndFunctionOperatorPrecedence);
+ }
+
+ emitter.Append(TextToken, ".");
+ emitter.Append(OperationToken, function);
+ emitter.AppendOpenParen();
+ GetExprText(rightExpr, emitter, settings);
+ emitter.AppendCloseParen();
+}
+
+
+void PseudoRustFunction::AppendTwoOperandFunctionWithCarry(const string& function,
+ const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings)
+{
+ const auto leftExpr = instr.GetLeftExpr();
+ const auto rightExpr = instr.GetRightExpr();
+ const auto carryExpr = instr.GetCarryExpr();
+
+ tokens.Append(OperationToken, function);
+ AppendSingleSizeToken(instr.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+
+ if (leftExpr.operation == HLIL_SPLIT)
+ {
+ const auto low = leftExpr.GetLowExpr();
+ const auto high = leftExpr.GetHighExpr();
+
+ tokens.Append(OperationToken, "COMBINE");
+ tokens.AppendOpenParen();
+ GetExprText(high, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprText(low, tokens, settings);
+ tokens.AppendCloseParen();
+ }
+
+ GetExprText(leftExpr, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprText(rightExpr, tokens, settings);
+ tokens.Append(TextToken, ", ");
+ GetExprText(carryExpr, tokens, settings);
+
+ tokens.AppendCloseParen();
+}
+
+
+Ref<Type> PseudoRustFunction::GetFieldType(const HighLevelILInstruction& var, bool deref)
+{
+ Ref<Type> type = var.GetType().GetValue();
+ if (deref && type && (type->GetClass() == PointerTypeClass))
+ type = type->GetChildType().GetValue();
+
+ if (type && (type->GetClass() == NamedTypeReferenceClass))
+ type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference());
+
+ return type;
+}
+
+
+PseudoRustFunction::FieldDisplayType PseudoRustFunction::GetFieldDisplayType(
+ Ref<Type> type, uint64_t offset, size_t memberIndex, bool deref)
+{
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure*, size_t, uint64_t, uint64_t, const StructureMember&) {}),
+ memberIndexHint)
+ return FieldDisplayName;
+ return FieldDisplayOffset;
+ }
+ else if (deref || offset != 0)
+ return FieldDisplayMemberOffset;
+ else
+ return FieldDisplayNone;
+}
+
+
+void PseudoRustFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset,
+ size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref)
+{
+ const auto type = GetFieldType(var, deref);
+ const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref);
+ switch (fieldDisplayType)
+ {
+ case FieldDisplayName:
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ tokens.Append(OperationToken, ".");
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0,
+ BN_FULL_CONFIDENCE, nameList);
+ }),
+ memberIndexHint)
+ return;
+
+ // Part of structure but no defined field, use __offset syntax
+ tokens.Append(OperationToken, ".");
+ char offsetStr[64];
+ snprintf(
+ offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", offset, Type::GetSizeSuffix(size).c_str());
+
+ vector<string> nameList {offsetStr};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList);
+
+ tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList);
+ return;
+ }
+
+ case FieldDisplayOffset:
+ {
+ /* this is handled before the display */
+ return;
+ }
+
+ case FieldDisplayMemberOffset:
+ {
+ tokens.AppendOpenBracket();
+ tokens.AppendIntegerTextToken(var, offset, size);
+ tokens.AppendCloseBracket();
+ return;
+ }
+
+ default: break;
+ }
+}
+
+
+bool PseudoRustFunction::IsMutable(const Variable& var) const
+{
+ for (auto i : GetHighLevelILFunction()->GetVariableDefinitions(var))
+ {
+ auto expr = GetHighLevelILFunction()->GetExpr(i);
+ if (expr.operation == HLIL_VAR_DECLARE || expr.operation == HLIL_VAR_INIT)
+ continue;
+ return true;
+ }
+ return GetHighLevelILFunction()->GetAliasedVariables().count(var) != 0;
+}
+
+
+void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, ExpressionType exprType,
+ std::optional<bool> signedHint)
+{
+ // The lambdas in this function are here to reduce stack frame size of this function. Without them,
+ // complex expression can cause the process to crash from a stack overflow.
+ auto exprGuard = tokens.SetCurrentExpr(instr);
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendOpenParen();
+ tokens.AppendOpenParen();
+ for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform()))
+ {
+ tokens.Append(token);
+ }
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, " ");
+ }
+ if (settings && settings->IsOptionSet(ShowILOpcodes))
+ {
+ tokens.Append(OperationToken, "/*");
+ switch (instr.operation)
+ {
+ case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break;
+ case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break;
+ case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break;
+ case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break;
+ case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break;
+ case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break;
+ case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break;
+ case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break;
+ case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break;
+ case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break;
+ case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break;
+ case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break;
+ case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break;
+ case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break;
+ case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break;
+ case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break;
+ case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break;
+ case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break;
+ case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break;
+ case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break;
+ case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break;
+ case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break;
+ case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break;
+ case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break;
+ case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break;
+ case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break;
+ case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break;
+ case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break;
+ case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break;
+ case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break;
+ case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break;
+ case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break;
+ case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break;
+ case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break;
+ case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break;
+ case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break;
+ case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break;
+ case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break;
+ case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break;
+ case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break;
+ case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break;
+ case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break;
+ case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break;
+ case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break;
+ case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break;
+ case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break;
+ case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break;
+ case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break;
+ case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break;
+ case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break;
+ case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break;
+ case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break;
+ case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break;
+ case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break;
+ case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break;
+ case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break;
+ case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break;
+ case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break;
+ case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break;
+ case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break;
+ case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break;
+ case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break;
+ case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break;
+ case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break;
+ case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break;
+ case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break;
+ case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break;
+ case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break;
+ case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break;
+ case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break;
+ case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break;
+ case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break;
+ case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break;
+ case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break;
+ case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break;
+ case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break;
+ case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break;
+ case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break;
+ case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break;
+ case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break;
+ case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break;
+ case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break;
+ case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break;
+ case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break;
+ case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break;
+ case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break;
+ case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break;
+ case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break;
+ case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break;
+ case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break;
+ case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break;
+ case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break;
+ case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break;
+ case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break;
+ case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break;
+ case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break;
+ case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break;
+ case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break;
+ case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break;
+ case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break;
+ case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break;
+ case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break;
+ case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break;
+ case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break;
+ case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break;
+ case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break;
+ case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break;
+ case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break;
+ case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break;
+ case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break;
+ case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break;
+ case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break;
+ case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break;
+ case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break;
+ case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break;
+ case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break;
+ case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break;
+ case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break;
+ case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break;
+ case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break;
+ case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break;
+ case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break;
+ }
+ tokens.Append(OperationToken, "*/");
+ tokens.Append(TextToken, " ");
+ }
+
+ switch (instr.operation)
+ {
+ case HLIL_BLOCK:
+ [&]() {
+ const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>();
+ bool needSeparator = false;
+ for (auto i = exprs.begin(); i != exprs.end(); ++i)
+ {
+ // Don't show void returns at the very end of the function when printing
+ // the root of an AST, as it is implicit and almost always omitted in
+ // normal source code.
+ auto next = i;
+ ++next;
+ if (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) &&
+ (next == exprs.end()) && ((*i).operation == HLIL_RET) &&
+ ((*i).GetSourceExprs<HLIL_RET>().size() == 0))
+ continue;
+
+ // If the statement is one that contains additional blocks of code, insert a scope separator
+ // to visually separate the logic.
+ bool hasBlocks = false;
+ switch ((*i).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:
+ hasBlocks = true;
+ break;
+ default:
+ hasBlocks = false;
+ break;
+ }
+ if (needSeparator || (i != exprs.begin() && hasBlocks))
+ {
+ tokens.ScopeSeparator();
+ }
+ needSeparator = hasBlocks;
+
+ // Emit the lines for the statement itself
+ GetExprText(*i, tokens, settings, true, TopLevelOperatorPrecedence,
+ exprType == TrailingStatementExpression && next == exprs.end() ?
+ TrailingStatementExpression : StatementExpression);
+ tokens.NewLine();
+ }
+ }();
+ break;
+
+ case HLIL_FOR:
+ [&]() {
+ const auto initExpr = instr.GetInitExpr<HLIL_FOR>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_FOR>();
+ const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>();
+
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "for ");
+
+ // If the loop can be represented as a ranged for in idiomatic Rust, show it that way
+ if (initExpr.operation == HLIL_VAR_INIT &&
+ (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_SLE ||
+ condExpr.operation == HLIL_CMP_ULT || condExpr.operation == HLIL_CMP_ULE) &&
+ condExpr.GetLeftExpr().operation == HLIL_VAR &&
+ condExpr.GetLeftExpr().GetVariable<HLIL_VAR>() == initExpr.GetDestVariable<HLIL_VAR_INIT>() &&
+ updateExpr.operation == HLIL_ASSIGN &&
+ updateExpr.GetDestExpr<HLIL_ASSIGN>() == condExpr.GetLeftExpr() &&
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().operation == HLIL_ADD &&
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetLeftExpr<HLIL_ADD>() == condExpr.GetLeftExpr())
+ {
+ bool stepBy = updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().operation != HLIL_CONST ||
+ updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().GetConstant() != 1;
+
+ const auto variable = initExpr.GetDestVariable<HLIL_VAR_INIT>();
+ const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable);
+ tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName,
+ instr.exprIndex, variable.ToIdentifier(), instr.size);
+ tokens.Append(KeywordToken, " in ");
+
+ if (stepBy)
+ tokens.AppendOpenParen();
+
+ GetExprText(initExpr.GetSourceExpr<HLIL_VAR_INIT>(), tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_ULT)
+ tokens.Append(TextToken, "..");
+ else
+ tokens.Append(TextToken, "..=");
+ GetExprText(condExpr.GetRightExpr(), tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+
+ if (stepBy)
+ {
+ tokens.AppendCloseParen();
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "step_by");
+ tokens.AppendOpenParen();
+ GetExprText(updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>(),
+ tokens, settings, asFullAst);
+ tokens.AppendCloseParen();
+ }
+ }
+ else
+ {
+ // For loop isn't directly representable in standard Rust
+ if (initExpr.operation != HLIL_NOP)
+ GetExprText(initExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (condExpr.operation != HLIL_NOP)
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ tokens.Append(TextToken, "; ");
+ if (updateExpr.operation != HLIL_NOP)
+ GetExprText(updateExpr, tokens, settings, asFullAst);
+ }
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, StatementExpression);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.Append(KeywordToken, "while ");
+ GetExprText(condExpr, tokens, settings);
+ }
+ }();
+ 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>();
+
+ tokens.Append(KeywordToken, "if ");
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr);
+ tokens.BeginScope(scopeType);
+
+ GetExprText(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(scopeType);
+ //tokens.SetCurrentExpr(falseExpr);
+ if (falseExpr.operation == HLIL_IF)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else ");
+ GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ }
+ else if (falseExpr.operation != HLIL_NOP)
+ {
+ tokens.ScopeContinuation(false);
+ tokens.Append(KeywordToken, "else");
+ scopeType = HighLevelILFunction::GetExprScopeType(falseExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.FinalizeScope();
+ }
+ }();
+ break;
+
+ case HLIL_WHILE:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>();
+ const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>();
+
+ if (condExpr.operation == HLIL_CONST && condExpr.GetConstant<HLIL_CONST>() != 0)
+ {
+ tokens.Append(KeywordToken, "loop");
+ }
+ else
+ {
+ tokens.Append(KeywordToken, "while ");
+ GetExprText(condExpr, tokens, settings);
+ }
+ if (!asFullAst)
+ return;
+
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, StatementExpression);
+ tokens.EndScope(scopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_DO_WHILE:
+ [&]() {
+ const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>();
+ const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>();
+ if (asFullAst)
+ {
+ tokens.Append(KeywordToken, "do");
+ auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr);
+ tokens.BeginScope(scopeType);
+ GetExprText(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, StatementExpression);
+ tokens.EndScope(scopeType);
+ tokens.ScopeContinuation(true);
+ tokens.Append(KeywordToken, "while ");
+ GetExprText(condExpr, tokens, settings);
+ tokens.Append(KeywordToken, ";");
+ tokens.FinalizeScope();
+ }
+ else
+ {
+ tokens.Append(TextToken, "/* do */ ");
+ tokens.Append(KeywordToken, "while ");
+ GetExprText(condExpr, tokens, settings);
+ }
+ }();
+ break;
+
+ case HLIL_SWITCH:
+ [&]() {
+ const auto condExpr = instr.GetConditionExpr<HLIL_SWITCH>();
+ const auto caseExprs = instr.GetCases<HLIL_SWITCH>();
+ const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>();
+
+ tokens.Append(KeywordToken, "match ");
+ GetExprText(condExpr, tokens, settings, asFullAst);
+ tokens.BeginScope(SwitchScopeType);
+ if (!asFullAst)
+ return;
+
+ for (const auto caseExpr : caseExprs)
+ {
+ GetExprText(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.NewLine();
+ }
+
+ // Check for default case
+ if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE)
+ {
+ tokens.Append(TextToken, "_ =>");
+ tokens.BeginScope(CaseScopeType);
+ GetExprText(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(CaseScopeType);
+ tokens.FinalizeScope();
+ }
+
+ tokens.EndScope(SwitchScopeType);
+ tokens.FinalizeScope();
+
+ }();
+ break;
+
+ case HLIL_CASE:
+ [&]() {
+ const auto valueExprs = instr.GetValueExprs<HLIL_CASE>();
+ const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>();
+
+ for (size_t index{}; index < valueExprs.size(); index++)
+ {
+ const auto& valueExpr = valueExprs[index];
+ if (index != 0)
+ tokens.Append(TextToken, " | ");
+ GetExprText(valueExpr, tokens, settings, asFullAst);
+ }
+ tokens.Append(TextToken, " =>");
+
+ if (!asFullAst)
+ return;
+
+ tokens.BeginScope(CaseScopeType);
+ GetExprText(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType);
+ tokens.EndScope(CaseScopeType);
+ tokens.FinalizeScope();
+ }();
+ break;
+
+ case HLIL_BREAK:
+ [&]() {
+ tokens.Append(KeywordToken, "break");
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONTINUE:
+ [&]() {
+ tokens.Append(KeywordToken, "continue");
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ZX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ZX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence, InnerExpression, false);
+ tokens.Append(KeywordToken, " as ");
+ AppendSizeToken(instr.size, false, tokens);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_SX>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence, InnerExpression, true);
+ tokens.Append(KeywordToken, " as ");
+ AppendSizeToken(instr.size, true, tokens);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_CALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>();
+
+ GetExprText(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+
+ vector<FunctionParameter> namedParams;
+ Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType();
+ if (functionType && (functionType->GetClass() == PointerTypeClass)
+ && (functionType->GetChildType()->GetClass() == FunctionTypeClass))
+ namedParams = functionType->GetChildType()->GetParameters();
+
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+
+ // If the type of the parameter is known to be a pointer to a string, then we directly render it as a
+ // string, regardless of its length
+ bool renderedAsString = false;
+ if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR)
+ {
+ auto exprType = namedParams[index].type;
+ if (exprType && (exprType->GetClass() == PointerTypeClass))
+ {
+ if (auto child = exprType->GetChildType(); child)
+ {
+ if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1)
+ || child->IsWideChar())
+ {
+ tokens.AppendPointerTextToken(parameterExprs[index],
+ parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols,
+ precedence, true);
+ renderedAsString = true;
+ }
+ }
+ }
+ }
+
+ if (!renderedAsString)
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_IMPORT:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_IMPORT>();
+ auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant);
+ const auto symbolType = symbol->GetType();
+
+ if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol))
+ {
+ symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant);
+ const auto symbolShortName = symbol->GetShortName();
+ tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand);
+ return;
+ }
+
+ tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ARRAY_INDEX:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ARRAY_INDEX>();
+ const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>();
+
+ GetExprText(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenBracket();
+ GetExprText(indexExpr, tokens, settings, asFullAst);
+ tokens.AppendCloseBracket();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_INIT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>();
+ const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ RustTypePrinter printer;
+ const auto prevTypeTokens = variableType ?
+ printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken> {};
+ const auto postTypeTokens = variableType ?
+ printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken> {};
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (const auto ssaForm = instr.GetSSAForm(); ssaForm.operation == HLIL_VAR_INIT_SSA)
+ {
+ const auto ssaDest = ssaForm.GetDestSSAVariable<HLIL_VAR_INIT_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest);
+ }
+
+ // If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ if (appearsDead)
+ tokens.BeginForceZeroConfidence();
+
+ tokens.Append(KeywordToken, "let ");
+
+ // Only show `mut` keyword if the variable is actually changed
+ if (IsMutable(destExpr))
+ tokens.Append(KeywordToken, "mut ");
+
+ if (variableType)
+ {
+ for (auto typeToken : prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = destExpr.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.AppendVarTextToken(destExpr, instr, instr.size);
+ if (variableType)
+ {
+ for (auto typeToken : postTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = destExpr.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.Append(OperationToken, " = ");
+
+ // For the right side of the assignment, only use zero confidence if the instruction does
+ // not have any side effects
+ if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr))
+ {
+ tokens.EndForceZeroConfidence();
+ appearsDead = false;
+ }
+
+ GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR_DECLARE:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>();
+
+ const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable);
+ const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
+ RustTypePrinter printer;
+ const auto prevTypeTokens =
+ variableType ?
+ printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+ const auto postTypeTokens =
+ variableType ?
+ printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
+ vector<InstructionTextToken>{};
+
+ tokens.Append(KeywordToken, "let ");
+
+ // Only show `mut` keyword if the variable is actually changed
+ if (IsMutable(variable))
+ tokens.Append(KeywordToken, "mut ");
+
+ if (variableType)
+ {
+ for (auto typeToken: prevTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = variable.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+ tokens.AppendVarTextToken(variable, instr, instr.size);
+ if (variableType)
+ {
+ for (auto typeToken: postTypeTokens)
+ {
+ typeToken.context = LocalVariableTokenContext;
+ typeToken.address = variable.ToIdentifier();
+ tokens.Append(typeToken);
+ }
+ }
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONST:
+ [&]() {
+ const auto constant = instr.GetConstant<HLIL_FLOAT_CONST>();
+ if (instr.size == 4)
+ {
+ char valueStr[64];
+ union
+ {
+ float f;
+ uint32_t i;
+ } bits{};
+ bits.i = constant;
+ snprintf(valueStr, sizeof(valueStr), "%.9gf", bits.f);
+ tokens.Append(FloatingPointToken, InstructionAddressTokenContext, valueStr, instr.address);
+ }
+ else if (instr.size == 8)
+ {
+ char valueStr[64];
+ union
+ {
+ double f;
+ uint64_t i;
+ } bits{};
+ bits.i = constant;
+ snprintf(valueStr, sizeof(valueStr), "%.17g", bits.f);
+ string s = valueStr;
+ if ((s.find('.') == string::npos) && (s.find('e') == string::npos))
+ s += ".0";
+ tokens.Append(FloatingPointToken, InstructionAddressTokenContext, s, instr.address);
+ }
+ else
+ {
+ tokens.AppendIntegerTextToken(instr, constant, 8);
+ }
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST:
+ [&]() {
+ tokens.AppendConstantTextToken(instr, instr.GetConstant<HLIL_CONST>(), instr.size, settings, precedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_DATA:
+ [&]() {
+ // Constant data should be rendered according to the type of builtin function being used.
+ const ConstantData& data = instr.GetConstantData<HLIL_CONST_DATA>();
+ if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength())
+ {
+ switch (builtin)
+ {
+ case BuiltinStrcpy:
+ case BuiltinStrncpy:
+ {
+ string result(db.ToEscapedString(true));
+ tokens.Append(BraceToken, "\"");
+ tokens.Append(StringToken, ConstStringDataTokenContext, result, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ break;
+ }
+ case BuiltinMemset:
+ {
+ char buf[32];
+ if (data.value < 10)
+ snprintf(buf, sizeof(buf), "%" PRId64 "", data.value);
+ else
+ snprintf(buf, sizeof(buf), "0x%" PRIx64 "", data.value);
+
+ tokens.Append(BraceToken, "{");
+ tokens.Append(StringToken, ConstDataTokenContext, string(buf), instr.address, data.value);
+ tokens.Append(BraceToken, "}");
+ break;
+ }
+ default:
+ {
+ if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); unicode.has_value())
+ {
+ auto wideStringPrefix = (builtin == BuiltinWcscpy) ? "L" : "";
+ auto tokenContext = (builtin == BuiltinWcscpy) ? ConstStringDataTokenContext : ConstDataTokenContext;
+ tokens.Append(BraceToken, wideStringPrefix + string("\""));
+ tokens.Append(StringToken, tokenContext, unicode.value().first, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ }
+ else
+ {
+ string result(db.ToEscapedString(false, true));
+
+ tokens.Append(BraceToken, "\"");
+ tokens.Append(StringToken, ConstDataTokenContext, result, instr.address, data.value);
+ tokens.Append(BraceToken, "\"");
+ // TODO controls for emitting an initializer list?
+ // char str[32];
+ // string result;
+ // const uint8_t* bytes = (const uint8_t*)db.GetData();
+ // for (size_t i = 0; i < db.GetLength(); i++)
+ // {
+ // snprintf(str, sizeof(str), "0x%" PRIx8 ", ", bytes[i]);
+ // result += str;
+ // }
+ // if (result.size() > 2)
+ // result.erase(result.end() - 2);
+ // tokens.Append(StringToken, StringDisplayTokenContext, string("{ ") + result + string(" }"), instr.address, bytes.value);
+ }
+ break;
+ }
+ }
+ }
+ else
+ tokens.Append(StringToken, StringDisplayTokenContext, string("<invalid constant data>"), instr.address, data.value);
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CONST_PTR:
+ [&]() {
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_VAR:
+ [&]() {
+ const auto variable = instr.GetVariable<HLIL_VAR>();
+ const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable);
+ tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName,
+ instr.exprIndex, variable.ToIdentifier(), instr.size);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_ASSIGN>();
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>();
+
+ // Check to see if the variable appears live
+ bool appearsDead = false;
+ if (destExpr.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaForm);
+ }
+ else if (destExpr.operation == HLIL_VAR)
+ {
+ if (const auto ssaForm = destExpr.GetSSAForm(); ssaForm.operation == HLIL_VAR_SSA)
+ {
+ const auto ssaDest = ssaForm.GetSSAVariable<HLIL_VAR_SSA>();
+ appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest);
+ }
+ }
+
+ // If the variable does not appear live, show the assignment as zero confidence (grayed out)
+ if (appearsDead)
+ tokens.BeginForceZeroConfidence();
+
+ std::optional<string> assignUpdateOperator;
+ std::optional<HighLevelILInstruction> assignUpdateSource;
+ bool assignUpdateNegate = false;
+ const auto isSplit = destExpr.operation == HLIL_SPLIT;
+ std::optional<bool> assignSignHint;
+ if (isSplit)
+ {
+ const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ GetExprText(high, tokens, settings, asFullAst, precedence);
+ tokens.Append(OperationToken, " = ");
+ tokens.Append(OperationToken, "HIGH");
+ AppendSingleSizeToken(high.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ tokens.AppendCloseParen();
+ tokens.AppendSemicolon();
+ tokens.NewLine();
+ GetExprText(low, tokens, settings, asFullAst, precedence);
+ }
+ else
+ {
+ // Check for assignment with an operator on the same variable as the destination
+ // (for example, `a = a + 2` should be shown as `a += 2`)
+ if ((srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_SUB || srcExpr.operation == HLIL_MUL
+ || srcExpr.operation == HLIL_DIVU || srcExpr.operation == HLIL_DIVS
+ || srcExpr.operation == HLIL_LSL || srcExpr.operation == HLIL_LSR
+ || srcExpr.operation == HLIL_ASR || (instr.size != 0 && srcExpr.operation == HLIL_AND)
+ || (instr.size != 0 && srcExpr.operation == HLIL_OR)
+ || (instr.size != 0 && srcExpr.operation == HLIL_XOR))
+ && (srcExpr.GetLeftExpr() == destExpr))
+ {
+ auto lessThanZero = [](uint64_t value, uint64_t width) -> bool {
+ return ((1UL << ((width * 8) - 1UL)) & value) != 0;
+ };
+ switch (srcExpr.operation)
+ {
+ case HLIL_ADD:
+ assignUpdateOperator = " += ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+
+ if ((assignUpdateSource.value().operation == HLIL_CONST)
+ && lessThanZero(
+ assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size)
+ && assignUpdateSource.value().size >= instr.size)
+ {
+ // Convert addition of a negative constant into subtraction of a positive constant
+ assignUpdateOperator = " -= ";
+ assignUpdateNegate = true;
+ }
+ break;
+ case HLIL_SUB:
+ assignUpdateOperator = " -= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_MUL:
+ assignUpdateOperator = " *= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_DIVU:
+ assignUpdateOperator = " /= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ assignSignHint = false;
+ break;
+ case HLIL_DIVS:
+ assignUpdateOperator = " /= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ assignSignHint = false;
+ break;
+ case HLIL_LSL:
+ assignUpdateOperator = " <<= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_LSR:
+ assignUpdateOperator = " >>= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_ASR:
+ assignUpdateOperator = " >>= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_AND:
+ assignUpdateOperator = " &= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_OR:
+ assignUpdateOperator = " |= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ case HLIL_XOR:
+ assignUpdateOperator = " ^= ";
+ assignUpdateSource = srcExpr.GetRightExpr();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (
+ (srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_MUL
+ || (instr.size != 0 && srcExpr.operation == HLIL_AND)
+ || (instr.size != 0 && srcExpr.operation == HLIL_OR)
+ || (instr.size != 0 && srcExpr.operation == HLIL_XOR))
+ && (srcExpr.GetRightExpr() == destExpr))
+ {
+ switch (srcExpr.operation)
+ {
+ case HLIL_ADD:
+ assignUpdateOperator = " += ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_MUL:
+ assignUpdateOperator = " *= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_AND:
+ assignUpdateOperator = " &= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_OR:
+ assignUpdateOperator = " |= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ case HLIL_XOR:
+ assignUpdateOperator = " ^= ";
+ assignUpdateSource = srcExpr.GetLeftExpr();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ GetExprText(destExpr, tokens, settings, asFullAst, precedence);
+ if (assignUpdateOperator.has_value() && assignUpdateSource.has_value())
+ tokens.Append(OperationToken, assignUpdateOperator.value());
+ else
+ tokens.Append(OperationToken, " = ");
+
+ // For the right side of the assignment, only use zero confidence if the instruction does
+ // not have any side effects
+ if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr))
+ {
+ tokens.EndForceZeroConfidence();
+ appearsDead = false;
+ }
+
+ if (isSplit)
+ {
+// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>();
+ const auto low = destExpr.GetLowExpr<HLIL_SPLIT>();
+
+ tokens.Append(OperationToken, "LOW");
+ AppendSingleSizeToken(low.size, OperationToken, tokens);
+ tokens.AppendOpenParen();
+ }
+
+ if (assignUpdateOperator.has_value() && assignUpdateSource.has_value())
+ {
+ if (assignUpdateNegate)
+ {
+ tokens.AppendIntegerTextToken(assignUpdateSource.value(),
+ -BNSignExtend(
+ assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size, 8),
+ assignUpdateSource.value().size);
+ }
+ else
+ {
+ GetExprText(assignUpdateSource.value(), tokens, settings, asFullAst, AssignmentOperatorPrecedence,
+ InnerExpression, assignSignHint);
+ }
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, InnerExpression,
+ assignSignHint);
+ }
+
+ if (isSplit)
+ tokens.AppendCloseParen();
+
+ if (appearsDead)
+ tokens.EndForceZeroConfidence();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ASSIGN_UNPACK:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>();
+ const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>();
+ const auto firstExpr = destExprs[0];
+
+ GetExprText(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ tokens.Append(OperationToken, " = ");
+ GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_STRUCT_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>();
+ const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>();
+
+ const auto type = GetFieldType(srcExpr, false);
+ const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false);
+ if (fieldDisplayType == FieldDisplayOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ tokens.AppendOpenParen();
+
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "byte_offset");
+ tokens.AppendOpenParen();
+ tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size);
+ tokens.AppendCloseParen();
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TextToken, "*");
+ Ref<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ }
+
+ char offsetStr[64];
+ snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset);
+
+ vector<string> nameList { offsetStr };
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+ }
+ else if (fieldDisplayType == FieldDisplayMemberOffset)
+ {
+ tokens.Append(OperationToken, "*");
+ BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence;
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ srcPrecedence = LowUnaryOperatorPrecedence;
+ }
+ GetExprText(srcExpr, tokens, settings, true, srcPrecedence);
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TextToken, "*");
+ Ref<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ }
+ /* rest is rendered in AppendFieldTextTokens */
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ }
+
+ AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF:
+ [&]() {
+ auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>();
+
+ auto appendMaybeBrace = [&](const InstructionTextToken& token)
+ {
+ if (token.type == BraceToken)
+ {
+ if (token.text == "(")
+ tokens.AppendOpenParen();
+ else if (token.text == ")")
+ tokens.AppendCloseParen();
+ else if (token.text == "[")
+ tokens.AppendOpenBracket();
+ else if (token.text == "]")
+ tokens.AppendCloseBracket();
+ else if (token.text == "{")
+ tokens.AppendOpenBrace();
+ else if (token.text == "}")
+ tokens.AppendCloseBrace();
+ else
+ tokens.Append(token);
+ }
+ else
+ {
+ tokens.Append(token);
+ }
+ };
+
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+
+ const auto type = srcExpr.GetType();
+ BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence;
+ if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth() &&
+ (!settings || settings->IsOptionSet(ShowTypeCasts)))
+ srcPrecedence = LowUnaryOperatorPrecedence;
+
+ vector<InstructionTextToken> pointerTokens{};
+ if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, srcPrecedence) == DataSymbolResult)
+ {
+ if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth())
+ {
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ tokens.AppendOpenParen();
+
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TextToken, "*");
+ Ref<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(instr.size, false, tokens);
+ tokens.AppendCloseParen();
+ }
+ }
+ else
+ {
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+ }
+ }
+ else
+ {
+ for (const auto& token : pointerTokens)
+ {
+ appendMaybeBrace(token);
+ }
+ }
+ }
+ else
+ {
+ vector<bool> derefConst;
+ Ref<Type> srcType = srcExpr.GetType().GetValue();
+ derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst());
+ while (srcExpr.operation == HLIL_DEREF)
+ {
+ auto next = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ if (next.size == srcExpr.size)
+ {
+ srcType = srcExpr.GetType().GetValue();
+ derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst());
+ srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>();
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ for (size_t index = 0; index < derefConst.size(); index++)
+ tokens.Append(OperationToken, "*");
+
+ BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence;
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.AppendOpenParen();
+ srcPrecedence = LowUnaryOperatorPrecedence;
+ }
+
+ GetExprText(srcExpr, tokens, settings, asFullAst, srcPrecedence);
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ for (auto isConst : derefConst)
+ {
+ tokens.Append(TextToken, "*");
+ tokens.Append(KeywordToken, isConst ? "const ": "mut ");
+ }
+ AppendSizeToken(instr.size, false, tokens);
+
+ tokens.AppendCloseParen();
+ }
+
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_TAILCALL:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_TAILCALL>();
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>();
+
+ tokens.Append(AnnotationToken, "/* tailcall */");
+ tokens.NewLine();
+ if (exprType != TrailingStatementExpression)
+ tokens.Append(KeywordToken, "return ");
+ GetExprText(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendOpenParen();
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType == StatementExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADDRESS_OF:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "&");
+ GetExprText(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_E:
+ case HLIL_CMP_E:
+ [&]() {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_NE:
+ case HLIL_CMP_NE:
+ [&]() {
+ bool parens = precedence > EqualityOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_LT:
+ case HLIL_CMP_SLT:
+ case HLIL_CMP_ULT:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLT)
+ cmpSigned = true;
+ AppendComparison(" < ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_LE:
+ case HLIL_CMP_SLE:
+ case HLIL_CMP_ULE:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_ULE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SLE)
+ cmpSigned = true;
+ AppendComparison(" <= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_GE:
+ case HLIL_CMP_SGE:
+ case HLIL_CMP_UGE:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGE)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGE)
+ cmpSigned = true;
+ AppendComparison(" >= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_GT:
+ case HLIL_CMP_SGT:
+ case HLIL_CMP_UGT:
+ [&]() {
+ bool parens = precedence > CompareOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> cmpSigned;
+ if (instr.operation == HLIL_CMP_UGT)
+ cmpSigned = false;
+ else if (instr.operation == HLIL_CMP_SGT)
+ cmpSigned = true;
+ AppendComparison(" > ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_AND:
+ [&]() {
+ bool parens = instr.size == 0 ?
+ precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalOrOperatorPrecedence :
+ precedence >= EqualityOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(instr.size == 0 ? " && " : " & ", instr, tokens, settings, asFullAst,
+ instr.size == 0 ? LogicalAndOperatorPrecedence : BitwiseAndOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_OR:
+ [&]() {
+ bool parens = (instr.size == 0) ?
+ precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalAndOperatorPrecedence :
+ precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(instr.size == 0 ? " || " : " | ", instr, tokens, settings, asFullAst,
+ instr.size == 0 ? LogicalOrOperatorPrecedence : BitwiseOrOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_XOR:
+ [&]() {
+ bool parens = precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence ||
+ precedence == BitwiseOrOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" ^ ", instr, tokens, settings, asFullAst, BitwiseXorOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ADC:
+ case HLIL_ADD_OVERFLOW:
+ case HLIL_FADD:
+ case HLIL_ADD:
+ [&]() {
+ const auto leftType = instr.GetLeftExpr().GetType();
+ bool parens;
+ BNOperatorPrecedence opPrecedence = AddOperatorPrecedence;
+ if (leftType && leftType->IsPointer())
+ {
+ parens = false;
+ opPrecedence = MemberAndFunctionOperatorPrecedence;
+ }
+ else
+ {
+ parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ }
+
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" + ", instr, tokens, settings, asFullAst, opPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SUB:
+ [&]{
+ // Check for offset pointers
+ auto left = instr.GetLeftExpr<HLIL_SUB>();
+ auto right = instr.GetRightExpr<HLIL_SUB>();
+ if (left.operation == HLIL_VAR && right.operation == HLIL_CONST)
+ {
+ auto var = left.GetVariable<HLIL_VAR>();
+ auto srcOffset = right.GetConstant<HLIL_CONST>();
+ auto varType = GetFunction()->GetVariableType(var);
+ if (varType
+ && varType->GetClass() == PointerTypeClass
+ && varType->GetNamedTypeReference()
+ && varType->GetOffset() == srcOffset)
+ {
+ // Yes
+ tokens.Append(OperationToken, "ADJ");
+ tokens.AppendOpenParen();
+ GetExprText(left, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ tokens.AppendCloseParen();
+ return;
+ }
+ }
+
+ const auto leftType = instr.GetLeftExpr().GetType();
+ bool parens;
+ BNOperatorPrecedence opPrecedence = SubOperatorPrecedence;
+ if (leftType && leftType->IsPointer())
+ {
+ parens = false;
+ opPrecedence = MemberAndFunctionOperatorPrecedence;
+ }
+ else
+ {
+ parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ }
+
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, opPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+ case HLIL_SBB:
+ case HLIL_FSUB:
+ [&]() {
+ bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LSL:
+ [&]() {
+ bool parens = precedence > ShiftOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" << ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LSR:
+ case HLIL_ASR:
+ [&]() {
+ bool parens = precedence > ShiftOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ AppendTwoOperand(" >> ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_FMUL:
+ case HLIL_MUL:
+ case HLIL_MULU_DP:
+ case HLIL_MULS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence ||
+ precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence ||
+ precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> mulSigned;
+ if (instr.operation == HLIL_MULU_DP)
+ mulSigned = false;
+ else if (instr.operation == HLIL_MULS_DP)
+ mulSigned = true;
+ AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, MultiplyOperatorPrecedence, mulSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FDIV:
+ case HLIL_DIVU:
+ case HLIL_DIVU_DP:
+ case HLIL_DIVS:
+ case HLIL_DIVS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence
+ || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence
+ || precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> divSigned;
+ if (instr.operation == HLIL_DIVU || instr.operation == HLIL_DIVU_DP)
+ divSigned = false;
+ else if (instr.operation == HLIL_DIVS || instr.operation == HLIL_DIVS_DP)
+ divSigned = true;
+ AppendTwoOperand(" / ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, divSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_MODU:
+ case HLIL_MODU_DP:
+ case HLIL_MODS:
+ case HLIL_MODS_DP:
+ [&]() {
+ bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence
+ || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence
+ || precedence == BitwiseXorOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ std::optional<bool> modSigned;
+ if (instr.operation == HLIL_MODU || instr.operation == HLIL_MODU_DP)
+ modSigned = false;
+ else if (instr.operation == HLIL_MODS || instr.operation == HLIL_MODS_DP)
+ modSigned = true;
+ AppendTwoOperand(" % ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, modSigned);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_ROR:
+ [&]() {
+ AppendTwoOperandMethodCall("rotate_right", instr, tokens, settings);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ROL:
+ [&]() {
+ AppendTwoOperandMethodCall("rotate_left", instr, tokens, settings);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RLC:
+ [&]() {
+ AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RRC:
+ [&]() {
+ AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_TEST_BIT:
+ [&]() {
+ AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOOR:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_FLOOR>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "floor");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_CEIL:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_CEIL>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "ceil");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FTRUNC:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_FTRUNC>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "trunc");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FABS:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_FABS>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "abs");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FSQRT:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_FSQRT>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "sqrt");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_ROUND_TO_INT:
+ [&]() {
+ GetExprText(instr.GetSourceExpr<HLIL_ROUND_TO_INT>(), tokens, settings, MemberAndFunctionOperatorPrecedence);
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "round");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_O:
+ [&]() {
+ AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FCMP_UO:
+ [&]() {
+ AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+
+ case HLIL_NOT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_NOT>();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "!");
+ GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FNEG:
+ case HLIL_NEG:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr();
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ tokens.Append(OperationToken, "-");
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, InnerExpression, true);
+ tokens.AppendCloseParen();
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_CONV:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType = "f" + std::to_string(instr.size * 8);
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence);
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TypeNameToken, floatType.c_str());
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_FLOAT_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_TO_INT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence);
+ tokens.Append(KeywordToken, " as ");
+ AppendSizeToken(instr.size, true, tokens);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_BOOL_TO_INT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_BOOL_TO_INT>();
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence);
+ tokens.Append(KeywordToken, " as ");
+ AppendSizeToken(instr.size, true, tokens);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_INT_TO_FLOAT:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_INT_TO_FLOAT>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ const auto floatType = "f" + std::to_string(instr.size * 8);
+
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence);
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TypeNameToken, floatType.c_str());
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_INTRINSIC:
+ [&]() {
+ const auto intrinsic = instr.GetIntrinsic<HLIL_INTRINSIC>();
+ const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic);
+ const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>();
+
+ tokens.Append(KeywordToken, intrinsicName, intrinsic);
+ tokens.AppendOpenParen();
+ for (size_t index{}; index < parameterExprs.size(); index++)
+ {
+ const auto& parameterExpr = parameterExprs[index];
+ if (index != 0) tokens.Append(TextToken, ", ");
+ GetExprText(parameterExpr, tokens, settings, asFullAst);
+ }
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_RET:
+ [&]() {
+ const auto srcExprs = instr.GetSourceExprs<HLIL_RET>();
+
+ if (!asFullAst || exprType != TrailingStatementExpression)
+ tokens.Append(KeywordToken, "return");
+ if (srcExprs.size() != 0)
+ {
+ if (!asFullAst || exprType != TrailingStatementExpression)
+ tokens.Append(TextToken, " ");
+ if (srcExprs.size() > 1)
+ tokens.AppendOpenParen();
+ for (size_t index = 0; index < srcExprs.size(); index++)
+ {
+ const auto& srcExpr = srcExprs[index];
+ if (index != 0)
+ tokens.Append(TextToken, ", ");
+ GetExprText(srcExpr, tokens, settings, asFullAst);
+ }
+ if (srcExprs.size() > 1)
+ tokens.AppendCloseParen();
+ }
+ if (exprType == StatementExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_NORET:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* no return */");
+ }();
+ break;
+
+ case HLIL_UNREACHABLE:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* unreachable */");
+ }();
+ break;
+
+ case HLIL_JUMP:
+ [&]() {
+ const auto destExpr = instr.GetDestExpr<HLIL_JUMP>();
+ tokens.Append(AnnotationToken, "/* jump -> ");
+ GetExprText(destExpr, tokens, settings);
+ tokens.Append(AnnotationToken, " */");
+ }();
+ break;
+
+ case HLIL_UNDEF:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* undefined */");
+ }();
+ break;
+
+ case HLIL_TRAP:
+ [&]() {
+ const auto vector = instr.GetVector<HLIL_TRAP>();
+ tokens.Append(KeywordToken, "trap");
+ tokens.AppendOpenParen();
+ tokens.AppendIntegerTextToken(instr, vector, 8);
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_DEREF_FIELD:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_DEREF_FIELD>();
+ const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>();
+ const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>();
+ auto type = srcExpr.GetType().GetValue();
+
+ if (type && (type->GetClass() == PointerTypeClass))
+ type = type->GetChildType().GetValue();
+
+ if (type && (type->GetClass() == NamedTypeReferenceClass))
+ type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference());
+
+ bool derefOffset = false;
+ if (type && (type->GetClass() == StructureTypeClass))
+ {
+ std::optional<size_t> memberIndexHint;
+ if (memberIndex != BN_INVALID_EXPR)
+ memberIndexHint = memberIndex;
+
+ bool outer = true;
+ if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0,
+ [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset,
+ uint64_t adjustedOffset, const StructureMember& member) {
+ BNSymbolDisplayResult symbolType;
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+ symbolType = tokens.AppendPointerTextToken(
+ srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ symbolType = OtherSymbolResult;
+ }
+
+ const auto displayDeref = symbolType != DataSymbolResult;
+ if (displayDeref && outer)
+ tokens.Append(OperationToken, "->");
+ else
+ tokens.Append(OperationToken, ".");
+ outer = false;
+
+ vector<string> nameList {member.name};
+ HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(
+ GetFunction()->GetView(), type, srcExpr, nameList);
+
+ tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0,
+ BN_FULL_CONFIDENCE, nameList);
+ }),
+ memberIndexHint)
+ return;
+ }
+ else if (type && (type->GetClass() == StructureTypeClass))
+ {
+ derefOffset = true;
+ }
+
+ if (derefOffset || offset != 0)
+ {
+ bool parens = precedence > UnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+
+ tokens.Append(OperationToken, "*");
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ tokens.AppendOpenParen();
+
+ if (srcExpr.operation == HLIL_CONST_PTR)
+ {
+ const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>();
+ tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence);
+ }
+ else
+ {
+ GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence);
+ }
+
+ tokens.Append(TextToken, ".");
+ tokens.Append(OperationToken, "byte_offset");
+ tokens.AppendOpenParen();
+ tokens.AppendIntegerTextToken(instr, offset, instr.size);
+ tokens.AppendCloseParen();
+
+ if (!settings || settings->IsOptionSet(ShowTypeCasts))
+ {
+ tokens.Append(KeywordToken, " as ");
+ tokens.Append(TextToken, "*");
+ Ref<Type> srcType = srcExpr.GetType();
+ if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst())
+ tokens.Append(KeywordToken, "const ");
+ else
+ tokens.Append(KeywordToken, "mut ");
+ AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens);
+ tokens.AppendCloseParen();
+ }
+
+ if (parens)
+ tokens.AppendCloseParen();
+ }
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_EXTERN_PTR:
+ [&]() {
+ const int64_t val = instr.GetOffset<HLIL_EXTERN_PTR>();
+ if (val != 0)
+ tokens.AppendOpenParen();
+ tokens.AppendPointerTextToken(
+ instr, instr.GetConstant<HLIL_EXTERN_PTR>(), settings, AddressOfDataSymbols, precedence);
+ if (val != 0)
+ {
+ char valStr[32];
+ if (val >= 0)
+ {
+ tokens.Append(OperationToken, " + ");
+ if (val <= 9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val);
+ }
+ else
+ {
+ tokens.Append(OperationToken, " - ");
+ if (val >= -9)
+ snprintf(valStr, sizeof(valStr), "%" PRIx64, -val);
+ else
+ snprintf(valStr, sizeof(valStr), "0x%" PRIx64, -val);
+ }
+ tokens.Append(IntegerToken, valStr, val);
+ tokens.AppendCloseParen();
+ }
+
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SYSCALL:
+ [&]() {
+ tokens.Append(KeywordToken, "syscall");
+ tokens.AppendOpenParen();
+ const auto operandList = instr.GetParameterExprs<HLIL_SYSCALL>();
+ vector<FunctionParameter> namedParams;
+ bool skipSyscallNumber = false;
+ if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST))
+ {
+ const auto platform = GetFunction()->GetPlatform();
+ if (platform)
+ {
+ const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>();
+ const auto syscallName = platform->GetSystemCallName(syscall);
+ if (settings && settings->GetCallParameterHints() != NeverShowParameterHints)
+ {
+ const auto functionType = platform->GetSystemCallType(syscall);
+ if (functionType && (functionType->GetClass() == FunctionTypeClass))
+ namedParams = functionType->GetParameters();
+ }
+ if (syscallName.length())
+ {
+ tokens.Append(TextToken, syscallName);
+ tokens.Append(TextToken, " ");
+ tokens.AppendOpenBrace();
+ GetExprText(operandList[0], tokens, settings);
+ tokens.AppendCloseBrace();
+ skipSyscallNumber = true;
+ }
+ }
+ }
+ for (size_t i = (skipSyscallNumber ? 1 : 0); i < operandList.size(); i++)
+ {
+ if (i != 0)
+ tokens.Append(TextToken, ", ");
+ GetExprText(operandList[i], tokens, settings);
+ }
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_BP:
+ [&]() {
+ tokens.Append(KeywordToken, "breakpoint");
+ tokens.AppendOpenParen();
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_UNIMPL_MEM:
+ case HLIL_UNIMPL:
+ [&]() {
+ const auto hlilFunc = GetHighLevelILFunction();
+ const auto instructionText = hlilFunc->GetExprText(hlilFunc->GetInstruction(
+ hlilFunc->GetInstructionForExpr(instr.exprIndex)).exprIndex, true, settings);
+ tokens.Append(AnnotationToken, "/* ");
+ for (const auto& token : instructionText[0].tokens)
+ tokens.Append(token.type, token.text, token.value);
+
+ if (instructionText.size() > 1)
+ tokens.Append(AnnotationToken, "...");
+
+ tokens.Append(AnnotationToken, " */");
+ }();
+ break;
+
+ case HLIL_NOP:
+ [&]() {
+ tokens.Append(AnnotationToken, "/* nop */");
+ }();
+ break;
+
+ case HLIL_GOTO:
+ [&]() {
+ const auto target = instr.GetTarget<HLIL_GOTO>();
+ tokens.Append(KeywordToken, "goto ");
+ tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target);
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_LABEL:
+ [&]() {
+ const auto target = instr.GetTarget<HLIL_LABEL>();
+ tokens.DecreaseIndent();
+ tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target);
+ tokens.Append(TextToken, ":");
+ tokens.IncreaseIndent();
+ }();
+ break;
+
+ case HLIL_LOW_PART:
+ [&]() {
+ const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>();
+ if (settings && !settings->IsOptionSet(ShowTypeCasts))
+ {
+ GetExprText(srcExpr, tokens, settings, asFullAst, precedence);
+ return;
+ }
+ bool parens = precedence > LowUnaryOperatorPrecedence;
+ if (parens)
+ tokens.AppendOpenParen();
+ GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence);
+ tokens.Append(KeywordToken, " as ");
+ AppendSizeToken(instr.size, signedHint.value_or(true), tokens);
+ if (parens)
+ tokens.AppendCloseParen();
+ if (exprType != InnerExpression)
+ tokens.AppendSemicolon();
+ }();
+ break;
+
+ case HLIL_SPLIT: break;
+ default:
+ [&]() {
+ char buf[64] {};
+ snprintf(buf, sizeof(buf), "/* <UNIMPLEMENTED, %x> */", instr.operation);
+ tokens.Append(AnnotationToken, buf);
+ }();
+ break;
+ }
+
+ if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType())
+ {
+ tokens.AppendCloseParen();
+ }
+}
+
+
+void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens,
+ DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement)
+{
+ GetExprText(instr, tokens, settings, asFullAst, precedence, statement ? TrailingStatementExpression : InnerExpression);
+}
+
+
+string PseudoRustFunction::GetAnnotationStartString() const
+{
+ // Show annotations as Rust-style inline comments
+ return "/* ";
+}
+
+
+string PseudoRustFunction::GetAnnotationEndString() const
+{
+ // Show annotations as Rust-style inline comments
+ return " */";
+}
+
+
+PseudoRustFunctionType::PseudoRustFunctionType(): LanguageRepresentationFunctionType("Pseudo Rust")
+{
+ // Create a type printer for Rust-style types and register it
+ m_typePrinter = new RustTypePrinter();
+ TypePrinter::Register(m_typePrinter);
+}
+
+
+Ref<LanguageRepresentationFunction> PseudoRustFunctionType::Create(Architecture* arch, Function* owner,
+ HighLevelILFunction* highLevelILFunction)
+{
+ return new PseudoRustFunction(arch, owner, highLevelILFunction);
+}
+
+
+Ref<TypePrinter> PseudoRustFunctionType::GetTypePrinter()
+{
+ // Return the Rust type printer as the default type printer for this language
+ return m_typePrinter;
+}
+
+
+vector<DisassemblyTextLine> PseudoRustFunctionType::GetFunctionTypeTokens(Function* func, DisassemblySettings* settings)
+{
+ vector<DisassemblyTextLine> result;
+ DisassemblyTextLine line;
+ line.addr = func->GetStart();
+
+ RustTypePrinter printer;
+ Ref<Type> funcType = func->GetType();
+ if (!funcType)
+ return {};
+
+ // Use the Rust type printer to generate a Rust formatted function declaration
+ vector<InstructionTextToken> before = printer.GetTypeTokensBeforeName(funcType, func->GetPlatform());
+ vector<InstructionTextToken> after = printer.GetTypeTokensAfterNameInternal(funcType, func->GetPlatform(),
+ BN_FULL_CONFIDENCE, nullptr, NoTokenEscapingType, true);
+
+ line.tokens = before;
+ if (!before.empty())
+ line.tokens.emplace_back(TextToken, " ");
+
+ Ref<Symbol> sym = func->GetSymbol();
+ line.tokens.emplace_back(CodeSymbolToken, sym->GetShortName(), func->GetStart());
+
+ line.tokens.insert(line.tokens.end(), after.begin(), after.end());
+ return {line};
+}
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+#ifndef DEMO_VERSION
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ }
+#endif
+
+#ifdef DEMO_VERSION
+ bool PseudoCPluginInit()
+#else
+ BINARYNINJAPLUGIN bool CorePluginInit()
+#endif
+ {
+ LanguageRepresentationFunctionType* type = new PseudoRustFunctionType();
+ LanguageRepresentationFunctionType::Register(type);
+ return true;
+ }
+}
diff --git a/lang/rust/pseudorust.h b/lang/rust/pseudorust.h
new file mode 100644
index 00000000..a6494319
--- /dev/null
+++ b/lang/rust/pseudorust.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class PseudoRustFunction: public BinaryNinja::LanguageRepresentationFunction
+{
+ BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL;
+
+ enum FieldDisplayType
+ {
+ FieldDisplayName,
+ FieldDisplayOffset,
+ FieldDisplayMemberOffset,
+ FieldDisplayNone
+ };
+
+ enum ExpressionType
+ {
+ StatementExpression,
+ TrailingStatementExpression,
+ InnerExpression
+ };
+
+ BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref);
+ FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref);
+
+ BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings,
+ BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence);
+ std::string GetSizeToken(size_t size, bool isSigned);
+ void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter);
+ void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst,
+ BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt);
+ void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true);
+ void AppendTwoOperandMethodCall(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings);
+ void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size,
+ BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref);
+ bool IsMutable(const BinaryNinja::Variable& var) const;
+
+ void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst = true,
+ BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, ExpressionType exprType = InnerExpression,
+ std::optional<bool> signedHint = std::nullopt);
+
+protected:
+ virtual void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ virtual void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
+ BinaryNinja::DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) override;
+ virtual void BeginLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+ virtual void EndLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;
+
+public:
+ PseudoRustFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction);
+
+ std::string GetAnnotationStartString() const override;
+ std::string GetAnnotationEndString() const override;
+};
+
+class PseudoRustFunctionType: public BinaryNinja::LanguageRepresentationFunctionType
+{
+ BinaryNinja::Ref<BinaryNinja::TypePrinter> m_typePrinter;
+
+public:
+ PseudoRustFunctionType();
+ BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
+ BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
+ BinaryNinja::Ref<BinaryNinja::TypePrinter> GetTypePrinter() override;
+ std::vector<BinaryNinja::DisassemblyTextLine> GetFunctionTypeTokens(BinaryNinja::Function* func,
+ BinaryNinja::DisassemblySettings* settings) override;
+};
diff --git a/lang/rust/rusttypes.cpp b/lang/rust/rusttypes.cpp
new file mode 100644
index 00000000..c43817ab
--- /dev/null
+++ b/lang/rust/rusttypes.cpp
@@ -0,0 +1,339 @@
+#include <inttypes.h>
+#include "rusttypes.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+
+RustTypePrinter::RustTypePrinter(): TypePrinter("RustTypePrinter")
+{
+}
+
+
+void RustTypePrinter::AppendCallingConventionTokens(Type* type, Platform* platform, uint8_t baseConfidence,
+ vector<InstructionTextToken>& tokens)
+{
+ if (type->GetCallingConvention() && platform &&
+ type->GetCallingConvention().GetValue() != platform->GetDefaultCallingConvention())
+ {
+ uint8_t ccConfidence = type->GetCallingConvention().GetCombinedConfidence(baseConfidence);
+ tokens.emplace_back(baseConfidence, KeywordToken, "extern");
+ tokens.emplace_back(ccConfidence, TextToken, " ");
+ tokens.emplace_back(ccConfidence, BraceToken, "\"");
+ if (type->GetCallingConvention().GetValue() == platform->GetCdeclCallingConvention())
+ {
+ tokens.emplace_back(StringToken, StringDisplayTokenContext, "C", 0,
+ Utf8String, 0, BN_INVALID_OPERAND, ccConfidence);
+ }
+ else
+ {
+ tokens.emplace_back(StringToken, StringDisplayTokenContext, type->GetCallingConvention()->GetName(), 0,
+ Utf8String, 0, BN_INVALID_OPERAND, ccConfidence);
+ }
+ tokens.emplace_back(ccConfidence, BraceToken, "\"");
+ tokens.emplace_back(ccConfidence, TextToken, " ");
+ }
+}
+
+
+void RustTypePrinter::GetStructureMemberTokens(Platform* platform, Type* type, uint8_t baseConfidence,
+ BNTokenEscapingType escaping, vector<InstructionTextToken>& out)
+{
+ uint64_t offset = 0;
+ vector<StructureMember> members = type->GetStructure()->GetMembers();
+ for (size_t i = 0; i < members.size(); i++)
+ {
+ if (members[i].type->GetClass() == FunctionTypeClass)
+ continue;
+ if (i != 0)
+ out.emplace_back(baseConfidence, TextToken, ", ");
+ else
+ out.emplace_back(baseConfidence, TextToken, " ");
+
+ if ((!type->GetStructure()->IsPacked()) && (members[i].type->GetAlignment() != 0) &&
+ ((offset % members[i].type->GetAlignment()) != 0))
+ offset += members[i].type->GetAlignment() - (offset % members[i].type->GetAlignment());
+
+ if (members[i].offset < offset)
+ {
+ out.emplace_back(baseConfidence, KeywordToken, "__offset");
+ out.emplace_back(baseConfidence, BraceToken, "(");
+ out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, UnsignedHexadecimalDisplayType, members[i].offset, 8),
+ members[i].offset, 0, BN_INVALID_OPERAND, baseConfidence);
+ out.emplace_back(baseConfidence, BraceToken, ")");
+ offset = members[i].offset;
+ }
+ else if (members[i].offset > offset)
+ {
+ char offsetStr[32];
+ snprintf(offsetStr, sizeof(offsetStr), "_%" PRIx64, offset);
+ out.emplace_back(baseConfidence, KeywordToken, "__padding");
+ out.emplace_back(baseConfidence, TextToken, " ");
+ out.emplace_back(baseConfidence, TextToken, offsetStr);
+ out.emplace_back(baseConfidence, TextToken, ": ");
+ out.emplace_back(baseConfidence, BraceToken, "[");
+ out.emplace_back(baseConfidence, KeywordToken, "u8");
+ out.emplace_back(baseConfidence, TextToken, "; ");
+ out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, UnsignedHexadecimalDisplayType, members[i].offset - offset, 8),
+ members[i].offset - offset, 0, BN_INVALID_OPERAND, baseConfidence);
+ out.emplace_back(baseConfidence, BraceToken, "]");
+ offset = members[i].offset;
+ }
+
+ vector<InstructionTextToken> after =
+ GetTypeTokensAfterName(members[i].type, platform, BN_FULL_CONFIDENCE, nullptr, escaping);
+
+ out.emplace_back(baseConfidence, FieldNameToken,
+ NameList::EscapeTypeName(members[i].name, escaping));
+ out.insert(out.end(), after.begin(), after.end());
+
+ if (!type->GetStructure()->IsUnion())
+ offset = members[i].offset + members[i].type->GetWidth();
+ }
+ out.emplace_back(baseConfidence, TextToken, " ");
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensBeforeName(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping)
+{
+ vector<InstructionTextToken> tokens;
+ if ((!parentType || !parentType->IsPointer()) && type->GetClass() == FunctionTypeClass)
+ {
+ AppendCallingConventionTokens(type, platform, baseConfidence, tokens);
+ tokens.emplace_back(baseConfidence, KeywordToken, "fn");
+ }
+ return tokens;
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterName(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping)
+{
+ return GetTypeTokensAfterNameInternal(type, platform, baseConfidence, parentType, escaping);
+}
+
+
+vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterNameInternal(
+ Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType,
+ BNTokenEscapingType escaping, bool functionHeader)
+{
+ vector<InstructionTextToken> tokens;
+ if (!parentType && type->GetClass() != FunctionTypeClass)
+ tokens.emplace_back(TextToken, ": ");
+
+ switch (type->GetClass())
+ {
+ case FunctionTypeClass:
+ {
+ if (parentType && parentType->IsPointer())
+ {
+ AppendCallingConventionTokens(type, platform, baseConfidence, tokens);
+ tokens.emplace_back(baseConfidence, KeywordToken, "fn");
+ }
+
+ tokens.emplace_back(baseConfidence, BraceToken, "(");
+
+ auto params = type->GetParameters();
+ for (size_t i = 0; i < params.size(); i++)
+ {
+ if (i != 0)
+ tokens.emplace_back(baseConfidence, TextToken, ", ");
+
+ vector<InstructionTextToken> paramTokens = GetTypeTokensAfterName(params[i].type, platform,
+ params[i].type.GetCombinedConfidence(baseConfidence), type, escaping);
+
+ if (functionHeader)
+ {
+ for (auto& token : paramTokens)
+ {
+ token.context = LocalVariableTokenContext;
+ token.address = params[i].location.ToIdentifier();
+ }
+ }
+
+ InstructionTextToken nameToken;
+ if (params[i].name.length() == 0)
+ {
+ nameToken = InstructionTextToken(TextToken, "_");
+ }
+ else
+ {
+ nameToken = InstructionTextToken(ArgumentNameToken, NameList::EscapeTypeName(params[i].name, escaping), i);
+ if (functionHeader)
+ {
+ nameToken.context = LocalVariableTokenContext;
+ nameToken.address = params[i].location.ToIdentifier();
+ }
+ }
+ tokens.push_back(nameToken);
+ tokens.emplace_back(TextToken, ": ");
+ tokens.insert(tokens.end(), paramTokens.begin(), paramTokens.end());
+
+ if (!params[i].defaultLocation && platform)
+ {
+ switch (params[i].location.type)
+ {
+ case RegisterVariableSourceType:
+ {
+ string registerName = platform->GetArchitecture()->GetRegisterName((uint32_t)params[i].location.storage);
+ tokens.emplace_back(TextToken, " @ ");
+ tokens.emplace_back(RegisterToken, NameList::EscapeTypeName(registerName, escaping), params[i].location.storage);
+ break;
+ }
+ case FlagVariableSourceType:
+ {
+ string flagName = platform->GetArchitecture()->GetFlagName((uint32_t)params[i].location.storage);
+ tokens.emplace_back(TextToken, " @ ");
+ tokens.emplace_back(AnnotationToken, NameList::EscapeTypeName(flagName, escaping), params[i].location.storage);
+ break;
+ }
+ case StackVariableSourceType:
+ {
+ tokens.emplace_back(TextToken, " @ ");
+ char storageStr[32];
+ snprintf(storageStr, sizeof(storageStr), "%" PRIi64, params[i].location.storage);
+ tokens.emplace_back(IntegerToken, storageStr, params[i].location.storage);
+ break;
+ }
+ }
+ }
+ }
+
+ if (type->HasVariableArguments())
+ {
+ if (params.size() != 0)
+ tokens.emplace_back(TextToken, ", ");
+ tokens.emplace_back(type->HasVariableArguments().GetCombinedConfidence(baseConfidence), TextToken, "...");
+ }
+
+ tokens.emplace_back(baseConfidence, BraceToken, ")");
+
+ if (!type->CanReturn())
+ {
+ // No return functions are part of the Rust language, marked by returning the "never" type.
+ tokens.emplace_back(baseConfidence, TextToken, " -> ");
+ tokens.emplace_back(type->CanReturn().GetCombinedConfidence(baseConfidence), TextToken, "!");
+ }
+ else if (type->GetChildType() && type->GetChildType()->GetClass() != VoidTypeClass)
+ {
+ tokens.emplace_back(baseConfidence, TextToken, " -> ");
+ vector<InstructionTextToken> retn = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ if (functionHeader)
+ {
+ for (auto& i : retn)
+ i.context = FunctionReturnTokenContext;
+ }
+ tokens.insert(tokens.end(), retn.begin(), retn.end());
+ }
+ break;
+ }
+ case IntegerTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken,
+ (type->IsSigned() ? "i" : "u") + to_string(type->GetWidth() * 8));
+ break;
+ case FloatTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, "f" + to_string(type->GetWidth() * 8));
+ break;
+ case WideCharTypeClass:
+ if (type->GetWidth() == 4)
+ tokens.emplace_back(baseConfidence, TypeNameToken, "char");
+ else
+ tokens.emplace_back(baseConfidence, TypeNameToken, "char" + to_string(type->GetWidth() * 8));
+ break;
+ case BoolTypeClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, "bool");
+ break;
+ case VoidTypeClass:
+ if (parentType && parentType->IsPointer())
+ {
+ tokens.emplace_back(baseConfidence, TypeNameToken, "c_void");
+ }
+ else
+ {
+ tokens.emplace_back(baseConfidence, BraceToken, "(");
+ tokens.emplace_back(baseConfidence, BraceToken, ")");
+ }
+ break;
+ case StructureTypeClass:
+ if (type->GetRegisteredName())
+ {
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping));
+ }
+ else
+ {
+ tokens.emplace_back(baseConfidence, KeywordToken, "struct");
+ tokens.emplace_back(baseConfidence, TextToken, " ");
+ tokens.emplace_back(baseConfidence, BraceToken, "{");
+ GetStructureMemberTokens(platform, type, baseConfidence, escaping, tokens);
+ tokens.emplace_back(baseConfidence, BraceToken, "}");
+ }
+ break;
+ case EnumerationTypeClass:
+ if (type->GetRegisteredName())
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping));
+ else
+ tokens.emplace_back(baseConfidence, TypeNameToken, "i" + to_string(type->GetWidth() * 8));
+ break;
+ case VarArgsTypeClass:
+ tokens.emplace_back(baseConfidence, TextToken, "...");
+ break;
+ case PointerTypeClass:
+ {
+ if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(
+ type->GetChildType(), platform, baseConfidence, type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ return tokens;
+ }
+
+ tokens.emplace_back(baseConfidence, TextToken, "*");
+ if (type->GetChildType()->IsConst())
+ tokens.emplace_back(baseConfidence, KeywordToken, "const");
+ else
+ tokens.emplace_back(baseConfidence, KeywordToken, "mut");
+ tokens.emplace_back(baseConfidence, TextToken, " ");
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ break;
+ }
+ case ArrayTypeClass:
+ {
+ tokens.emplace_back(baseConfidence, BraceToken, "[");
+ vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform,
+ type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping);
+ tokens.insert(tokens.end(), inner.begin(), inner.end());
+ tokens.emplace_back(baseConfidence, TextToken, "; ");
+ tokens.emplace_back(ArrayIndexToken, DisassemblyTextRenderer::GetDisplayStringForInteger(
+ nullptr, type->GetIntegerTypeDisplayType(), type->GetElementCount(), 8).c_str(), type->GetElementCount());
+ tokens.emplace_back(baseConfidence, BraceToken, "]");
+ }
+ case ValueTypeClass:
+ tokens.emplace_back(baseConfidence, TextToken, NameList::EscapeTypeName(type->GetAlternateName(), escaping));
+ break;
+ case NamedTypeReferenceClass:
+ tokens.emplace_back(baseConfidence, TypeNameToken, type->GetNamedTypeReference()->GetName().GetString(escaping));
+ break;
+ default:
+ tokens.emplace_back(AnnotationToken, "/* invalid type */");
+ break;
+ }
+
+ return tokens;
+}
+
+
+vector<TypeDefinitionLine> RustTypePrinter::GetTypeLines(
+ Ref<Type> type, const TypeContainer& types, const QualifiedName& name, int paddingCols,
+ bool collapsed, BNTokenEscapingType escaping)
+{
+ // TODO: Implement this to get type rendering in the Types view
+ return {};
+}
diff --git a/lang/rust/rusttypes.h b/lang/rust/rusttypes.h
new file mode 100644
index 00000000..419e5253
--- /dev/null
+++ b/lang/rust/rusttypes.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+class RustTypePrinter: public BinaryNinja::TypePrinter
+{
+ void AppendCallingConventionTokens(BinaryNinja::Type* type, BinaryNinja::Platform* platform, uint8_t baseConfidence,
+ std::vector<BinaryNinja::InstructionTextToken>& tokens);
+ void GetStructureMemberTokens(BinaryNinja::Platform* platform, BinaryNinja::Type* type, uint8_t baseConfidence,
+ BNTokenEscapingType escaping, std::vector<BinaryNinja::InstructionTextToken>& out);
+
+public:
+ RustTypePrinter();
+
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensBeforeName(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterName(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+ std::vector<BinaryNinja::TypeDefinitionLine> GetTypeLines(
+ BinaryNinja::Ref<BinaryNinja::Type> type, const BinaryNinja::TypeContainer& types,
+ const BinaryNinja::QualifiedName& name, int paddingCols = 64, bool collapsed = false,
+ BNTokenEscapingType escaping = NoTokenEscapingType) override;
+
+ std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterNameInternal(
+ BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform,
+ uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
+ BNTokenEscapingType escaping = NoTokenEscapingType, bool functionHeader = false);
+};