diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-08-22 12:55:49 -0600 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-10-21 13:56:55 -0400 |
| commit | d8e3001e535fad178c621ff07418f81f25123dc4 (patch) | |
| tree | 54c03cef2f0bdfd9a3b6df4334c81becb63e4993 /lang | |
| parent | 0e281a30d73c0f31ef9442fef0346779164231ad (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.txt | 45 | ||||
| -rw-r--r-- | lang/c/pseudoc.cpp | 2852 | ||||
| -rw-r--r-- | lang/c/pseudoc.h | 66 | ||||
| -rw-r--r-- | lang/rust/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | lang/rust/pseudorust.cpp | 2925 | ||||
| -rw-r--r-- | lang/rust/pseudorust.h | 79 | ||||
| -rw-r--r-- | lang/rust/rusttypes.cpp | 339 | ||||
| -rw-r--r-- | lang/rust/rusttypes.h | 32 |
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); +}; |
