diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2025-01-20 18:44:25 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2025-01-20 18:44:25 -0500 |
| commit | e78cae77103b5396ce42d9e33593ea55f9135be0 (patch) | |
| tree | b44cb4bf7732c1e2e77ae39335eb4e2f02d9af84 | |
| parent | cbd4d7f12d54ddc4b6d3d90a8d7b49591f468a94 (diff) | |
Revert "Add line formatter API and a generic line formatter plugin"
This reverts commit 1699c71999d29d32aba5c9f8fea193a661a4b02b.
| -rw-r--r-- | basicblock.cpp | 18 | ||||
| -rw-r--r-- | binaryninjaapi.h | 93 | ||||
| -rw-r--r-- | binaryninjacore.h | 59 | ||||
| -rw-r--r-- | formatter/generic/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | formatter/generic/genericformatter.cpp | 895 | ||||
| -rw-r--r-- | formatter/generic/genericformatter.h | 14 | ||||
| -rw-r--r-- | lang/c/pseudoc.cpp | 8 | ||||
| -rw-r--r-- | lang/c/pseudoc.h | 3 | ||||
| -rw-r--r-- | lang/rust/pseudorust.cpp | 8 | ||||
| -rw-r--r-- | lang/rust/pseudorust.h | 3 | ||||
| -rw-r--r-- | languagerepresentation.cpp | 33 | ||||
| -rw-r--r-- | lineformatter.cpp | 225 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/examples/pseudo_python.py | 13 | ||||
| -rw-r--r-- | python/function.py | 12 | ||||
| -rw-r--r-- | python/highlevelil.py | 53 | ||||
| -rw-r--r-- | python/languagerepresentation.py | 34 | ||||
| -rw-r--r-- | python/lineformatter.py | 291 |
18 files changed, 50 insertions, 1758 deletions
diff --git a/basicblock.cpp b/basicblock.cpp index 2b58e30e..6bdd93ae 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -36,24 +36,6 @@ DisassemblySettings::DisassemblySettings(BNDisassemblySettings* settings) } -Ref<DisassemblySettings> DisassemblySettings::GetDefaultSettings() -{ - return new DisassemblySettings(BNDefaultDisassemblySettings()); -} - - -Ref<DisassemblySettings> DisassemblySettings::GetDefaultGraphSettings() -{ - return new DisassemblySettings(BNDefaultGraphDisassemblySettings()); -} - - -Ref<DisassemblySettings> DisassemblySettings::GetDefaultLinearSettings() -{ - return new DisassemblySettings(BNDefaultLinearDisassemblySettings()); -} - - DisassemblySettings* DisassemblySettings::Duplicate() { return new DisassemblySettings(BNDuplicateDisassemblySettings(m_object)); diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 9d3c3dc8..2a2e5e9b 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -10171,10 +10171,6 @@ namespace BinaryNinja { DisassemblySettings(BNDisassemblySettings* settings); DisassemblySettings* Duplicate(); - static Ref<DisassemblySettings> GetDefaultSettings(); - static Ref<DisassemblySettings> GetDefaultGraphSettings(); - static Ref<DisassemblySettings> GetDefaultLinearSettings(); - bool IsOptionSet(BNDisassemblyOption option) const; void SetOption(BNDisassemblyOption option, bool state = true); @@ -13698,82 +13694,6 @@ namespace BinaryNinja { std::set<SSAVariable> GetSSAVariables(); }; - struct LineFormatterSettings - { - Ref<HighLevelILFunction> highLevelIL; - size_t desiredLineLength; - size_t minimumContentLength; - size_t tabWidth; - std::string languageName; - std::string commentStartString; - std::string commentEndString; - std::string annotationStartString; - std::string annotationEndString; - - /*! Gets the default line formatter settings for High Level IL code. - - \param settings The settings for reformatting. - \param func High Level IL function to be reformatted. - \return Settings for reformatting. - */ - static LineFormatterSettings GetDefault(DisassemblySettings* settings, HighLevelILFunction* func); - - /*! Gets the default line formatter settings for a language representation function. - - \param settings The settings for reformatting. - \param func Language representation function to be reformatted. - \return Settings for reformatting. - */ - static LineFormatterSettings GetLanguageRepresentationSettings( - DisassemblySettings* settings, LanguageRepresentationFunction* func); - - static LineFormatterSettings FromAPIObject(const BNLineFormatterSettings* settings); - BNLineFormatterSettings ToAPIObject() const; - }; - - class LineFormatter : public StaticCoreRefCountObject<BNLineFormatter> - { - std::string m_nameForRegister; - - static BNDisassemblyTextLine* FormatLinesCallback(void* ctxt, BNDisassemblyTextLine* inLines, size_t inCount, - const BNLineFormatterSettings* settings, size_t* outCount); - static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count); - - public: - LineFormatter(const std::string& name); - LineFormatter(BNLineFormatter* formatter); - - /*! Registers the line formatter. - - \param formatter The line formatter to register. - */ - static void Register(LineFormatter* formatter); - - static std::vector<Ref<LineFormatter>> GetList(); - static Ref<LineFormatter> GetByName(const std::string& name); - static Ref<LineFormatter> GetDefault(); - - /*! Reformats the given list of lines. Returns a new list of lines containing the reformatted code. - - \param lines The lines to reformat. - \param settings The settings for reformatting. - \return A new list of reformatted lines. - */ - virtual std::vector<DisassemblyTextLine> FormatLines( - const std::vector<DisassemblyTextLine>& lines, const LineFormatterSettings& settings) = 0; - }; - - class CoreLineFormatter : public LineFormatter - { - public: - CoreLineFormatter(BNLineFormatter* formatter); - - std::vector<DisassemblyTextLine> FormatLines( - const std::vector<DisassemblyTextLine>& lines, const LineFormatterSettings& settings) override; - }; - - class LanguageRepresentationFunctionType; - /*! LanguageRepresentationFunction represents a single function in a registered high level language. \ingroup highlevelil @@ -13783,8 +13703,7 @@ namespace BinaryNinja { BNFreeLanguageRepresentationFunction> { public: - LanguageRepresentationFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* func, - HighLevelILFunction* highLevelIL); + LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL); LanguageRepresentationFunction(BNLanguageRepresentationFunction* func); /*! Gets the lines of tokens for a given High Level IL instruction. @@ -13823,7 +13742,6 @@ namespace BinaryNinja { */ BNHighlightColor GetHighlight(BasicBlock* block); - Ref<LanguageRepresentationFunctionType> GetLanguage() const; Ref<Architecture> GetArchitecture() const; Ref<Function> GetFunction() const; Ref<HighLevelILFunction> GetHighLevelILFunction() const; @@ -13972,13 +13890,6 @@ namespace BinaryNinja { */ virtual Ref<TypeParser> GetTypeParser() { return nullptr; } - /*! Returns the line formatter for formatting code in this language. If NULL is returned, the default - formatter will be used. - - \return The optional formatter for formatting code in this language. - */ - virtual Ref<LineFormatter> GetLineFormatter() { return nullptr; } - /*! Returns a list of lines representing a function prototype in this language. If no lines are returned, the default C-style prototype will be used. @@ -14005,7 +13916,6 @@ namespace BinaryNinja { static bool IsValidCallback(void* ctxt, BNBinaryView* view); static BNTypePrinter* GetTypePrinterCallback(void* ctxt); static BNTypeParser* GetTypeParserCallback(void* ctxt); - static BNLineFormatter* GetLineFormatterCallback(void* ctxt); static BNDisassemblyTextLine* GetFunctionTypeTokensCallback( void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count); static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count); @@ -14020,7 +13930,6 @@ namespace BinaryNinja { bool IsValid(BinaryView* view) override; Ref<TypePrinter> GetTypePrinter() override; Ref<TypeParser> GetTypeParser() override; - Ref<LineFormatter> GetLineFormatter() override; std::vector<DisassemblyTextLine> GetFunctionTypeTokens( Function* func, DisassemblySettings* settings = nullptr) override; }; diff --git a/binaryninjacore.h b/binaryninjacore.h index 5ac55e6b..b13a7a7a 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 89 +#define BN_CURRENT_CORE_ABI_VERSION 88 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 89 +#define BN_MINIMUM_CORE_ABI_VERSION 86 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -302,7 +302,6 @@ extern "C" typedef struct BNDemangler BNDemangler; typedef struct BNFirmwareNinja BNFirmwareNinja; typedef struct BNFirmwareNinjaReferenceNode BNFirmwareNinjaReferenceNode; - typedef struct BNLineFormatter BNLineFormatter; //! Console log levels typedef enum BNLogLevel @@ -728,7 +727,6 @@ extern "C" HighLevelILLinearDisassembly = 65, WaitForIL = 66, IndentHLILBody = 67, - DisableLineFormatting = 68, // Debugging options ShowFlagUsage = 128, @@ -3441,7 +3439,6 @@ extern "C" bool (*isValid)(void* ctxt, BNBinaryView* view); BNTypePrinter* (*getTypePrinter)(void* ctxt); BNTypeParser* (*getTypeParser)(void* ctxt); - BNLineFormatter* (*getLineFormatter)(void* ctxt); BNDisassemblyTextLine* (*getFunctionTypeTokens)( void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count); void (*freeLines)(void* ctxt, BNDisassemblyTextLine* lines, size_t count); @@ -3541,28 +3538,6 @@ extern "C" size_t unique; } BNFirmwareNinjaDeviceAccesses; - typedef struct BNLineFormatterSettings - { - BNHighLevelILFunction* highLevelIL; - size_t desiredLineLength; - size_t minimumContentLength; - size_t tabWidth; - char* languageName; - char* commentStartString; - char* commentEndString; - char* annotationStartString; - char* annotationEndString; - } BNLineFormatterSettings; - - typedef struct BNCustomLineFormatter - { - void* context; - BNDisassemblyTextLine* (*formatLines)(void* ctxt, BNDisassemblyTextLine* inLines, size_t inCount, - const BNLineFormatterSettings* settings, size_t* outCount); - void (*freeLines)(void* ctxt, BNDisassemblyTextLine* lines, size_t count); - } BNCustomLineFormatter; - - BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI char* BNAllocStringWithLength(const char* contents, size_t len); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -5478,9 +5453,6 @@ extern "C" // Disassembly settings BINARYNINJACOREAPI BNDisassemblySettings* BNCreateDisassemblySettings(void); - BINARYNINJACOREAPI BNDisassemblySettings* BNDefaultDisassemblySettings(void); - BINARYNINJACOREAPI BNDisassemblySettings* BNDefaultGraphDisassemblySettings(void); - BINARYNINJACOREAPI BNDisassemblySettings* BNDefaultLinearDisassemblySettings(void); BINARYNINJACOREAPI BNDisassemblySettings* BNNewDisassemblySettingsReference(BNDisassemblySettings* settings); BINARYNINJACOREAPI BNDisassemblySettings* BNDuplicateDisassemblySettings(BNDisassemblySettings* settings); BINARYNINJACOREAPI void BNFreeDisassemblySettings(BNDisassemblySettings* settings); @@ -6171,19 +6143,15 @@ extern "C" BNLanguageRepresentationFunctionType* type, BNBinaryView* view); BINARYNINJACOREAPI BNTypePrinter* BNGetLanguageRepresentationFunctionTypePrinter(BNLanguageRepresentationFunctionType* type); BINARYNINJACOREAPI BNTypeParser* BNGetLanguageRepresentationFunctionTypeParser(BNLanguageRepresentationFunctionType* type); - BINARYNINJACOREAPI BNLineFormatter* BNGetLanguageRepresentationFunctionTypeLineFormatter( - BNLanguageRepresentationFunctionType* type); BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens( BNLanguageRepresentationFunctionType* type, BNFunction* func, BNDisassemblySettings* settings, size_t* count); BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNCreateCustomLanguageRepresentationFunction( - BNLanguageRepresentationFunctionType* type, BNArchitecture* arch, BNFunction* func, - BNHighLevelILFunction* highLevelIL, BNCustomLanguageRepresentationFunction* callbacks); + BNArchitecture* arch, BNFunction* func, BNHighLevelILFunction* highLevelIL, + BNCustomLanguageRepresentationFunction* callbacks); BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNNewLanguageRepresentationFunctionReference( BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI void BNFreeLanguageRepresentationFunction(BNLanguageRepresentationFunction* func); - BINARYNINJACOREAPI BNLanguageRepresentationFunctionType* BNGetLanguageRepresentationType( - BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI BNArchitecture* BNGetLanguageRepresentationArchitecture(BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI BNFunction* BNGetLanguageRepresentationOwnerFunction(BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI BNHighLevelILFunction* BNGetLanguageRepresentationILFunction(BNLanguageRepresentationFunction* func); @@ -8088,25 +8056,6 @@ extern "C" BINARYNINJACOREAPI void BNFreeFirmwareNinjaReferenceNode(BNFirmwareNinjaReferenceNode* node); BINARYNINJACOREAPI BNFirmwareNinjaReferenceNode* BNNewFirmwareNinjaReferenceNodeReference(BNFirmwareNinjaReferenceNode* node); BINARYNINJACOREAPI void BNFreeFirmwareNinjaReferenceNodes(BNFirmwareNinjaReferenceNode** nodes, size_t count); - - // Line formatters - BINARYNINJACOREAPI BNLineFormatter* BNRegisterLineFormatter(const char* name, BNCustomLineFormatter* callbacks); - BINARYNINJACOREAPI BNLineFormatter** BNGetLineFormatterList(size_t* count); - BINARYNINJACOREAPI void BNFreeLineFormatterList(BNLineFormatter** formatters); - BINARYNINJACOREAPI BNLineFormatter* BNGetLineFormatterByName(const char* name); - BINARYNINJACOREAPI BNLineFormatter* BNGetDefaultLineFormatter(); - - BINARYNINJACOREAPI char* BNGetLineFormatterName(BNLineFormatter* formatter); - - BINARYNINJACOREAPI BNDisassemblyTextLine* BNFormatLines(BNLineFormatter* formatter, BNDisassemblyTextLine* inLines, - size_t inCount, const BNLineFormatterSettings* settings, size_t* outCount); - - BINARYNINJACOREAPI BNLineFormatterSettings* BNGetDefaultLineFormatterSettings( - BNDisassemblySettings* settings, BNHighLevelILFunction* func); - BINARYNINJACOREAPI BNLineFormatterSettings* BNGetLanguageRepresentationLineFormatterSettings( - BNDisassemblySettings* settings, BNLanguageRepresentationFunction* func); - BINARYNINJACOREAPI void BNFreeLineFormatterSettings(BNLineFormatterSettings* settings); - #ifdef __cplusplus } #endif diff --git a/formatter/generic/CMakeLists.txt b/formatter/generic/CMakeLists.txt deleted file mode 100644 index 85a5a3e2..00000000 --- a/formatter/generic/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -cmake_minimum_required(VERSION 3.9 FATAL_ERROR) - -project(formatter_generic) - -if(NOT BN_INTERNAL_BUILD) - add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) -endif() - -file(GLOB SOURCES - *.cpp - *.h) - -if(DEMO) - add_library(formatter_generic STATIC ${SOURCES}) -else() - add_library(formatter_generic SHARED ${SOURCES}) -endif() - -target_include_directories(formatter_generic - PRIVATE ${PROJECT_SOURCE_DIR}) - -if(WIN32) - target_link_directories(formatter_generic - PRIVATE ${BN_INSTALL_DIR}) - target_link_libraries(formatter_generic binaryninjaapi binaryninjacore) -else() - target_link_libraries(formatter_generic binaryninjaapi) -endif() - -set_target_properties(formatter_generic 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(formatter_generic) - set_target_properties(formatter_generic PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} - RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) -endif() diff --git a/formatter/generic/genericformatter.cpp b/formatter/generic/genericformatter.cpp deleted file mode 100644 index e82f6c9b..00000000 --- a/formatter/generic/genericformatter.cpp +++ /dev/null @@ -1,895 +0,0 @@ -#include <ctype.h> -#include <stack> -#include "genericformatter.h" - -using namespace BinaryNinja; -using namespace std; - - -enum ItemType -{ - Atom, - Comment, - Operator, - FieldAccessor, - Argument, - ArgumentSeparator, - Statement, - StatementSeparator, - Group, - Container, - StartOfContainer, - ContainerContents, - EndOfContainer, -}; - - -static bool IsTokenWhitespace(const InstructionTextToken& token) -{ - if (token.type == AddressDisplayToken || token.type == AddressSeparatorToken - || token.type == CollapseStateIndicatorToken) - return true; - - for (auto ch : token.text) - { - if (!isspace(ch)) - return false; - } - return true; -} - - -static string TrimString(const string& str) -{ - size_t startPos = 0; - size_t endPos = 0; - - bool start = true; - for (size_t i = 0; i < str.size(); i++) - { - if (isspace(str[i])) - { - if (start) - { - startPos = i + 1; - endPos = i + 1; - } - } - else - { - start = false; - endPos = i + 1; - } - } - - return str.substr(startPos, endPos - startPos); -} - - -static string TrimLeadingWhitespace(const string& str) -{ - size_t startPos = 0; - for (size_t i = 0; i < str.size(); i++) - { - if (!isspace(str[i])) - { - startPos = i; - break; - } - } - return str.substr(startPos); -} - - -static string TrimTrailingWhitespace(const string& str) -{ - if (str.empty()) - return str; - - size_t endPos = str.size(); - for (size_t i = str.size() - 1; i > 0; i--) - { - if (!isspace(str[i])) - { - endPos = i + 1; - break; - } - } - return str.substr(0, endPos); -} - - -static const map<string, BNOperatorPrecedence> g_operatorPrecedenceMap = {{"=", AssignmentOperatorPrecedence}, - {":=", AssignmentOperatorPrecedence}, {"+=", AssignmentOperatorPrecedence}, {"-=", AssignmentOperatorPrecedence}, - {"*=", AssignmentOperatorPrecedence}, {"/=", AssignmentOperatorPrecedence}, {"s/=", AssignmentOperatorPrecedence}, - {"u/=", AssignmentOperatorPrecedence}, {"%=", AssignmentOperatorPrecedence}, {"s%=", AssignmentOperatorPrecedence}, - {"u%=", AssignmentOperatorPrecedence}, {"&=", AssignmentOperatorPrecedence}, {"|=", AssignmentOperatorPrecedence}, - {"^=", AssignmentOperatorPrecedence}, {">>=", AssignmentOperatorPrecedence}, {"s>>=", AssignmentOperatorPrecedence}, - {"u>>=", AssignmentOperatorPrecedence}, {"<<=", AssignmentOperatorPrecedence}, - {"s<<=", AssignmentOperatorPrecedence}, {"u<<=", AssignmentOperatorPrecedence}, {"?", TernaryOperatorPrecedence}, - {":", TernaryOperatorPrecedence}, {"||", LogicalOrOperatorPrecedence}, {"or", LogicalOrOperatorPrecedence}, - {"&&", LogicalAndOperatorPrecedence}, {"and", LogicalAndOperatorPrecedence}, {"&", BitwiseAndOperatorPrecedence}, - {"|", BitwiseOrOperatorPrecedence}, {"^", BitwiseXorOperatorPrecedence}, {"==", EqualityOperatorPrecedence}, - {"===", EqualityOperatorPrecedence}, {"!=", EqualityOperatorPrecedence}, {"!==", EqualityOperatorPrecedence}, - {"<>", EqualityOperatorPrecedence}, {"<", CompareOperatorPrecedence}, {"s<", CompareOperatorPrecedence}, - {"u<", CompareOperatorPrecedence}, {"<=", CompareOperatorPrecedence}, {"s<=", CompareOperatorPrecedence}, - {"u<=", CompareOperatorPrecedence}, {">", CompareOperatorPrecedence}, {"s>", CompareOperatorPrecedence}, - {"u>", CompareOperatorPrecedence}, {">=", CompareOperatorPrecedence}, {"s>=", CompareOperatorPrecedence}, - {"u>=", CompareOperatorPrecedence}, {"<<", ShiftOperatorPrecedence}, {"s<<", ShiftOperatorPrecedence}, - {"u<<", ShiftOperatorPrecedence}, {">>", ShiftOperatorPrecedence}, {"s>>", ShiftOperatorPrecedence}, - {"u>>", ShiftOperatorPrecedence}, {"+", AddOperatorPrecedence}, {"-", AddOperatorPrecedence}, - {"*", MultiplyOperatorPrecedence}, {"/", MultiplyOperatorPrecedence}, {"s/", MultiplyOperatorPrecedence}, - {"u/", MultiplyOperatorPrecedence}, {"%", MultiplyOperatorPrecedence}, {"s%", MultiplyOperatorPrecedence}, - {"u%", MultiplyOperatorPrecedence}, {"!", UnaryOperatorPrecedence}, {"not", UnaryOperatorPrecedence}, - {"~", UnaryOperatorPrecedence}}; - - -static BNOperatorPrecedence GetOperatorPrecedence(const InstructionTextToken& token, size_t* ternary = nullptr) -{ - string trimmedText = TrimString(token.text); - auto i = g_operatorPrecedenceMap.find(trimmedText); - if (i != g_operatorPrecedenceMap.end()) - { - if (i->second == TernaryOperatorPrecedence && ternary) - { - // HLIL uses ':' in additional contexts, so look for active ternary operators before - // treating it as part of a ternary - if (trimmedText == "?") - { - (*ternary)++; - } - else if (trimmedText == ":") - { - if (*ternary) - (*ternary)--; - else - return MemberAndFunctionOperatorPrecedence; - } - } - return i->second; - } - return MemberAndFunctionOperatorPrecedence; -} - - -struct Item -{ - ItemType type; - vector<Item> items; - vector<InstructionTextToken> tokens; - size_t width; - - void AppendAllTokens(vector<InstructionTextToken>& output, bool& firstTokenOfLine) - { - if (firstTokenOfLine) - { - if (!tokens.empty()) - { - InstructionTextToken token = tokens.front(); - string trimmedText = TrimLeadingWhitespace(token.text); - token.width -= token.text.size() - trimmedText.size(); - token.text = trimmedText; - output.push_back(token); - output.insert(output.end(), tokens.begin() + 1, tokens.end()); - firstTokenOfLine = false; - } - } - else - { - output.insert(output.end(), tokens.begin(), tokens.end()); - } - - for (auto& item : items) - item.AppendAllTokens(output, firstTokenOfLine); - } - - void AddTokenToLastAtom(const InstructionTextToken& token) - { - if (!tokens.empty()) - tokens.push_back(token); - else if (items.empty()) - items.push_back(Item {Atom, {}, {token}, 0}); - else - items.back().AddTokenToLastAtom(token); - } - - void CalculateWidth() - { - width = 0; - for (auto& token : tokens) - width += token.width; - for (auto& item : items) - { - item.CalculateWidth(); - width += item.width; - } - } -}; - - -struct ItemLayoutStackEntry -{ - vector<Item> items; - size_t additionalContinuationIndentation; - size_t desiredWidth; - size_t desiredContinuationWidth; - bool newLineOnReenteringScope; -}; - - -static void CalculateWidthAndIndentation(const DisassemblyTextLine& line, size_t* totalLength, size_t* indentation) -{ - if (totalLength) - *totalLength = 0; - if (indentation) - *indentation = 0; - - bool inIndentation = true; - for (auto& token : line.tokens) - { - if (inIndentation) - { - if (token.type == AddressDisplayToken || token.type == AddressSeparatorToken - || token.type == CollapseStateIndicatorToken || IsTokenWhitespace(token)) - { - if (indentation) - *indentation += token.width; - } - else - { - inIndentation = false; - } - } - - if (totalLength) - *totalLength += token.width; - } -} - - -static vector<Item> CreateStatementItems(const vector<Item>& items) -{ - vector<Item> result, pending; - bool hasArgs = false; - for (auto& i : items) - { - if (i.type == StatementSeparator) - { - if (pending.empty()) - { - result.push_back(Item {Atom, {}, {i.tokens}, 0}); - } - else - { - for (auto& j : i.tokens) - pending.back().AddTokenToLastAtom(j); - result.push_back(Item {Statement, pending, {}, 0}); - } - pending.clear(); - hasArgs = true; - } - else if (i.type == StartOfContainer && pending.empty()) - { - result.push_back(i); - } - else if (i.type == EndOfContainer && hasArgs && !pending.empty()) - { - result.push_back(Item {Statement, pending, {}, 0}); - result.push_back(i); - pending.clear(); - } - else - { - pending.push_back(Item {i.type, CreateStatementItems(i.items), i.tokens, 0}); - } - } - - if (!pending.empty()) - { - if (hasArgs) - result.push_back(Item {Statement, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - } - - return result; -} - - -static vector<Item> CreateAssignmentOperatorGroups(const vector<Item>& items) -{ - vector<Item> result, pending; - bool hasOperators = false; - for (auto& i : items) - { - if (i.type == Operator && !i.tokens.empty()) - { - BNOperatorPrecedence precedence = GetOperatorPrecedence(i.tokens[0]); - if (precedence == AssignmentOperatorPrecedence) - { - if (pending.empty()) - { - result.push_back(Item {Atom, {}, {i.tokens}, 0}); - } - else - { - for (auto& j : i.tokens) - pending.back().AddTokenToLastAtom(j); - result.push_back(Item {Statement, pending, {}, 0}); - } - pending.clear(); - hasOperators = true; - continue; - } - } - - if (i.type == StartOfContainer && pending.empty()) - { - result.push_back(i); - } - else if (i.type == EndOfContainer && hasOperators && !pending.empty()) - { - result.push_back(Item {Group, pending, {}, 0}); - result.push_back(i); - pending.clear(); - } - else - { - pending.push_back(Item {i.type, CreateAssignmentOperatorGroups(i.items), i.tokens, 0}); - } - } - - if (!pending.empty()) - { - if (hasOperators) - result.push_back(Item {Group, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - } - - return result; -} - - -static vector<Item> CreateArgumentItems(const vector<Item>& items, bool inContainer) -{ - vector<Item> result, pending; - bool hasArgs = false; - for (auto& i : items) - { - if (i.type == ArgumentSeparator) - { - if (pending.empty()) - { - result.push_back(Item {Atom, {}, {i.tokens}, 0}); - } - else - { - for (auto& j : i.tokens) - pending.back().AddTokenToLastAtom(j); - result.push_back(Item {inContainer ? Argument : Group, pending, {}, 0}); - } - pending.clear(); - hasArgs = true; - } - else if (i.type == StartOfContainer && pending.empty()) - { - result.push_back(i); - } - else if (i.type == EndOfContainer && hasArgs && !pending.empty()) - { - result.push_back(Item {inContainer ? Argument : Group, pending, {}, 0}); - result.push_back(i); - pending.clear(); - } - else - { - pending.push_back(Item {i.type, CreateArgumentItems(i.items, i.type == Container), i.tokens, 0}); - } - } - - if (!pending.empty()) - { - if (hasArgs) - result.push_back(Item {inContainer ? Argument : Group, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - } - - return result; -} - - -static vector<Item> CreateOperatorGroups(const vector<Item>& items) -{ - vector<Item> result, pending; - bool hasOperators = false; - for (auto& i : items) - { - if (i.type == Operator) - { - if (pending.size() == 1) - result.push_back(pending[0]); - else if (!pending.empty()) - result.push_back(Item {Group, pending, {}, 0}); - result.push_back(i); - pending.clear(); - hasOperators = true; - continue; - } - - if (i.type == StartOfContainer && pending.empty()) - { - result.push_back(i); - } - else if (i.type == EndOfContainer && hasOperators && pending.size() > 1) - { - result.push_back(Item {Group, pending, {}, 0}); - result.push_back(i); - pending.clear(); - } - else - { - pending.push_back(Item {i.type, CreateOperatorGroups(i.items), i.tokens, 0}); - } - } - - if (!pending.empty()) - { - if (hasOperators && pending.size() > 1) - result.push_back(Item {Group, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - } - - return result; -} - - -static vector<Item> CreateOperatorPrecedenceGroups(const vector<Item>& items) -{ - // Look for the operator with the lowest precedence. These will be grouped first. - optional<BNOperatorPrecedence> lowestPrecedence; - size_t ternary = 0; - for (auto i = items.begin(); i != items.end(); ++i) - { - if (i != items.begin() && i->type == Operator && !i->tokens.empty()) - { - BNOperatorPrecedence precedence = GetOperatorPrecedence(i->tokens[0], &ternary); - if (!lowestPrecedence.has_value() || precedence < lowestPrecedence.value()) - lowestPrecedence = precedence; - } - } - - // If there were no operators, no need to group at this level. Just traverse down into child items. - if (!lowestPrecedence.has_value()) - { - vector<Item> result; - result.reserve(items.size()); - for (auto& i : items) - result.push_back({i.type, CreateOperatorPrecedenceGroups(i.items), i.tokens, 0}); - return result; - } - - // Go through the items and split the items into groups around the lowest precedence operator - vector<Item> result, pending; - ternary = 0; - for (auto i = items.begin(); i != items.end(); ++i) - { - if (i != items.begin() && i->type == Operator && !i->tokens.empty()) - { - BNOperatorPrecedence precedence = GetOperatorPrecedence(i->tokens[0], &ternary); - if (precedence == lowestPrecedence.value()) - { - if (pending.size() == 1) - result.push_back(pending[0]); - else if (!pending.empty()) - result.push_back(Item {Group, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - pending.clear(); - } - } - - if (i->type == StartOfContainer && pending.empty()) - { - result.push_back(*i); - } - else if (i->type == EndOfContainer && pending.size() > 1 && !result.empty()) - { - result.push_back(Item {Group, pending, {}, 0}); - result.push_back(*i); - pending.clear(); - } - else - { - pending.push_back(*i); - } - } - - if (!pending.empty()) - { - if (pending.size() > 1 && !result.empty()) - result.push_back(Item {Group, pending, {}, 0}); - else - result.insert(result.end(), pending.begin(), pending.end()); - } - - // Recurse into these groups and process the next lowest precedence in each - vector<Item> processed; - processed.reserve(result.size()); - for (auto& i : result) - processed.push_back({i.type, CreateOperatorPrecedenceGroups(i.items), i.tokens, 0}); - return processed; -} - - -static vector<Item> RelocateStartAndEndOfContainerItems(const vector<Item>& items) -{ - vector<Item> result; - for (auto& i : items) - { - if (!result.empty() && i.type == Container && !i.items.empty() && i.items.front().type == StartOfContainer) - { - Item startOfContainer = i.items.front(); - for (auto& j : startOfContainer.tokens) - result.back().AddTokenToLastAtom(j); - - vector<Item> containerItems(i.items.begin() + 1, i.items.end()); - containerItems = RelocateStartAndEndOfContainerItems(containerItems); - result.push_back({Container, containerItems, {}, 0}); - } - else if (i.type == EndOfContainer && !result.empty()) - { - for (auto& j : i.tokens) - result.back().AddTokenToLastAtom(j); - } - else - { - result.push_back(Item {i.type, RelocateStartAndEndOfContainerItems(i.items), i.tokens, 0}); - } - } - return result; -} - - -GenericLineFormatter::GenericLineFormatter(): LineFormatter("GenericLineFormatter") -{ -} - - -vector<DisassemblyTextLine> GenericLineFormatter::FormatLines( - const vector<DisassemblyTextLine>& lines, const LineFormatterSettings& settings) -{ - vector<DisassemblyTextLine> result; - for (size_t i = 0; i < lines.size(); i++) - { - const DisassemblyTextLine& currentLine = lines[i]; - - size_t totalLength, indentation; - CalculateWidthAndIndentation(currentLine, &totalLength, &indentation); - - // Check width against settings - size_t contentLength = totalLength - indentation; - if (totalLength <= settings.desiredLineLength || contentLength <= settings.minimumContentLength) - { - // Line fits, emit as-is - result.push_back(currentLine); - continue; - } - - // Calculate indentation for continuation lines. If the next line in the input is more indented, make - // the continuation lines more indented than that to separate the continuation from the new scope. - size_t continuationIndentation = indentation + settings.tabWidth; - if ((i + 1) < lines.size()) - { - size_t nextLineIndentation; - CalculateWidthAndIndentation(lines[i + 1], nullptr, &nextLineIndentation); - if (nextLineIndentation > indentation) - continuationIndentation = nextLineIndentation + settings.tabWidth; - } - size_t additionalContinuationIndentation = continuationIndentation - indentation; - - // Compute the target length for this line - size_t desiredWidth = settings.minimumContentLength; - if (indentation < settings.desiredLineLength) - { - size_t remainingWidth = settings.desiredLineLength - indentation; - if (remainingWidth > desiredWidth) - desiredWidth = remainingWidth; - } - - // Compute the target length for the continuation lines - size_t desiredContinuationWidth = settings.minimumContentLength; - if (continuationIndentation < settings.desiredLineLength) - { - size_t remainingWidth = settings.desiredLineLength - continuationIndentation; - if (remainingWidth > desiredContinuationWidth) - desiredContinuationWidth = remainingWidth; - } - - // Gather the indentation tokens at the beginning of the line - vector<InstructionTextToken> indentationTokens; - size_t tokenIndex = 0; - for (tokenIndex = 0; tokenIndex < currentLine.tokens.size(); tokenIndex++) - { - if (IsTokenWhitespace(currentLine.tokens[tokenIndex])) - indentationTokens.push_back(currentLine.tokens[tokenIndex]); - else - break; - } - - // First break the line down into nested container items. A container is anything between a pair of - // BraceTokens (except for strings, where the entire string, including the quotes, are treated as - // a single atom). - vector<Item> items; - stack<vector<Item>> itemStack; - for (; tokenIndex < currentLine.tokens.size(); tokenIndex++) - { - const InstructionTextToken& token = currentLine.tokens[tokenIndex]; - string trimmedText = TrimString(token.text); - - switch (token.type) - { - case BraceToken: - if (tokenIndex + 1 < currentLine.tokens.size() - && currentLine.tokens[tokenIndex + 1].type == StringToken) - { - // Treat string tokens surrounded by brace tokens as a unit (this is usually the quotes - // surrounding the string) - Item atom; - atom.type = Atom; - atom.tokens.push_back(token); - atom.tokens.push_back(currentLine.tokens[tokenIndex + 1]); - atom.width = 0; - tokenIndex++; - if (tokenIndex + 1 < currentLine.tokens.size() - && currentLine.tokens[tokenIndex + 1].type == BraceToken) - { - atom.tokens.push_back(currentLine.tokens[tokenIndex + 1]); - tokenIndex++; - } - - items.push_back(atom); - break; - } - - if (trimmedText == "(" || trimmedText == "[" || trimmedText == "{") - { - // Create a ContainerContents item and place it onto the item stack. This will hold anything - // inside the container once the end of the container is found. - items.push_back(Item {Container, {}, {}, 0}); - itemStack.push(items); - - // Starting a new context - items.clear(); - items.push_back(Item {StartOfContainer, {}, {token}, 0}); - } - else if (trimmedText == ")" || trimmedText == "]" || trimmedText == "}") - { - items.push_back(Item {EndOfContainer, {}, {token}, 0}); - - if (itemStack.empty()) - break; - - // Go back up the item stack and add the items to the container - vector<Item> parent = itemStack.top(); - itemStack.pop(); - parent.back().items.insert(parent.back().items.end(), items.begin(), items.end()); - items = parent; - } - break; - case CommentToken: - { - // The rest of the line is a comment. There may be tokens that are not of CommentToken type, but - // these are used to create clickable items when things are referenced by the comment. - Item comment {Comment, {}, {}, 0}; - for (; tokenIndex < currentLine.tokens.size(); tokenIndex++) - comment.tokens.push_back(currentLine.tokens[tokenIndex]); - items.push_back(comment); - break; - } - case TextToken: - if (trimmedText == ",") - items.push_back(Item {ArgumentSeparator, {}, {token}, 0}); - else if ((!trimmedText.empty() && trimmedText[0] == '.') || trimmedText == "->") - items.push_back(Item {FieldAccessor, {}, {token}, 0}); - else if (trimmedText == ";") - items.push_back(Item {StatementSeparator, {}, {token}, 0}); - else if (trimmedText == ":" && !items.empty()) - items.back().AddTokenToLastAtom(token); - else - items.push_back(Item {Atom, {}, {token}, 0}); - break; - case OperationToken: - if ((!trimmedText.empty() && trimmedText[0] == '.') || trimmedText == "->") - items.push_back(Item {FieldAccessor, {}, {token}, 0}); - else - items.push_back(Item {Operator, {}, {token}, 0}); - break; - default: - items.push_back(Item {Atom, {}, {token}, 0}); - break; - } - } - - while (!itemStack.empty()) - { - vector<Item> parent = itemStack.top(); - itemStack.pop(); - parent.back().items.insert(parent.back().items.end(), items.begin(), items.end()); - items = parent; - } - - // Process the items to find semicolons, and create statement items containing the group of items making - // up each statement. - items = CreateStatementItems(items); - - // Process the items to find assignment operators, and group up the source and destination items. This needs - // to be done before creating arguments to better handle multiple return value constructs. - items = CreateAssignmentOperatorGroups(items); - - // Process the items to find commas, and create argument items containing the group of items making - // up each argument. - items = CreateArgumentItems(items, false); - - // Process the items to find operators, and create group items containing the operands - items = CreateOperatorGroups(items); - - // Process the items to group operations by operator precedence - items = CreateOperatorPrecedenceGroups(items); - - // Move start of container items to the last token of the previous item, and end of container items to - // the previous atom. - items = RelocateStartAndEndOfContainerItems(items); - - // Now that items are done, compute widths for layout - for (auto& j : items) - j.CalculateWidth(); - - DisassemblyTextLine outputLine = currentLine; - outputLine.tokens = indentationTokens; - size_t currentWidth = 0; - bool firstTokenOfLine = true; - - stack<ItemLayoutStackEntry> layoutStack; - layoutStack.push({items, additionalContinuationIndentation, desiredWidth, desiredContinuationWidth, false}); - - auto newLine = [&]() { - if (!firstTokenOfLine) - { - string lastTokenText = outputLine.tokens.back().text; - string trimmedText = TrimTrailingWhitespace(lastTokenText); - outputLine.tokens.back().width -= lastTokenText.size() - trimmedText.size(); - outputLine.tokens.back().text = trimmedText; - } - - result.push_back(outputLine); - outputLine.tokens = indentationTokens; - - // Make sure any collapsible state indicators are set to padding so that the indicators don't - // show up more than once for a single scope. - for (auto& outToken : outputLine.tokens) - { - if (outToken.type == CollapseStateIndicatorToken) - outToken.context = ContentCollapsiblePadding; - } - - outputLine.tokens.emplace_back(TextToken, string(additionalContinuationIndentation, ' ')); - currentWidth = 0; - desiredWidth = desiredContinuationWidth; - firstTokenOfLine = true; - }; - - while (!layoutStack.empty()) - { - ItemLayoutStackEntry layoutStackEntry = layoutStack.top(); - layoutStack.pop(); - - items = layoutStackEntry.items; - additionalContinuationIndentation = layoutStackEntry.additionalContinuationIndentation; - desiredWidth = layoutStackEntry.desiredWidth; - desiredContinuationWidth = layoutStackEntry.desiredContinuationWidth; - - // Check to see if the scope we are returning to needs a new line. This is used when an argument - // spans multiple lines. The rest of the arguments are placed on separate lines from the long argument. - if (layoutStackEntry.newLineOnReenteringScope && currentWidth > 0) - newLine(); - - for (auto item = items.begin(); item != items.end();) - { - if (currentWidth + item->width > desiredWidth) - { - // Current item is too wide to fit on the current line, will need to start a new line. - auto next = item; - ++next; - - // If we are already on a fresh line, or the item is too wide to fit on a new line of its - // own, we have to start emitting tokens and wrap in the middle of the item. If the item - // is a container, always use the splitting behavior. - if (currentWidth == 0 || item->width > desiredContinuationWidth || item->type == Container) - { - if (item->type == Argument && currentWidth != 0) - { - // If an argument is too wide to show on a single line all by itself, start the argument - // on a new line, and add additional indentation for the continuation of the argument. - if (next != items.end()) - { - layoutStack.push({vector(next, items.end()), additionalContinuationIndentation, - desiredWidth, desiredContinuationWidth, true}); - } - - newLine(); - - additionalContinuationIndentation += settings.tabWidth; - if (desiredContinuationWidth < settings.minimumContentLength + settings.tabWidth) - desiredContinuationWidth = settings.minimumContentLength; - else - desiredContinuationWidth -= settings.tabWidth; - - layoutStack.push({item->items, additionalContinuationIndentation, desiredWidth, - desiredContinuationWidth, false}); - break; - } - - if (item->tokens.empty()) - { - // Item contains other items. Place the context onto the layout stack and resume processing. - if (next != items.end()) - { - layoutStack.push({vector(next, items.end()), additionalContinuationIndentation, - desiredWidth, desiredContinuationWidth, false}); - } - layoutStack.push({item->items, additionalContinuationIndentation, desiredWidth, - desiredContinuationWidth, false}); - break; - } - - // Item is an atom. We just have to emit the tokens even though it is too wide. - item->AppendAllTokens(outputLine.tokens, firstTokenOfLine); - ++item; - continue; - } - - // Start a new line and add the item on the fresh line. - newLine(); - continue; - } - - // Item fits, emit all tokens for it - item->AppendAllTokens(outputLine.tokens, firstTokenOfLine); - currentWidth += item->width; - ++item; - } - } - - // Emit the last line if it had tokens - if (currentWidth > 0) - newLine(); - } - - return result; -} - - -extern "C" -{ - BN_DECLARE_CORE_ABI_VERSION - -#ifndef DEMO_EDITION - BINARYNINJAPLUGIN void CorePluginDependencies() - { - } -#endif - -#ifdef DEMO_EDITION - bool GenericFormatterPluginInit() -#else - BINARYNINJAPLUGIN bool CorePluginInit() -#endif - { - GenericLineFormatter* formatter = new GenericLineFormatter(); - LineFormatter::Register(formatter); - return true; - } -} diff --git a/formatter/generic/genericformatter.h b/formatter/generic/genericformatter.h deleted file mode 100644 index d48758ee..00000000 --- a/formatter/generic/genericformatter.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "binaryninjaapi.h" - - -class GenericLineFormatter: public BinaryNinja::LineFormatter -{ -public: - GenericLineFormatter(); - - std::vector<BinaryNinja::DisassemblyTextLine> FormatLines( - const std::vector<BinaryNinja::DisassemblyTextLine>& lines, - const BinaryNinja::LineFormatterSettings& settings) override; -}; diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp index 52622f57..dff591ff 100644 --- a/lang/c/pseudoc.cpp +++ b/lang/c/pseudoc.cpp @@ -6,9 +6,9 @@ using namespace std; using namespace BinaryNinja; -PseudoCFunction::PseudoCFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner, - HighLevelILFunction* highLevelILFunction) : - LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) +PseudoCFunction::PseudoCFunction( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : + LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) { } @@ -2766,7 +2766,7 @@ PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType(" Ref<LanguageRepresentationFunction> PseudoCFunctionType::Create(Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) { - return new PseudoCFunction(this, arch, owner, highLevelILFunction); + return new PseudoCFunction(arch, owner, highLevelILFunction); } diff --git a/lang/c/pseudoc.h b/lang/c/pseudoc.h index feb64552..024be2f1 100644 --- a/lang/c/pseudoc.h +++ b/lang/c/pseudoc.h @@ -51,8 +51,7 @@ protected: const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; public: - PseudoCFunction(BinaryNinja::LanguageRepresentationFunctionType* type, BinaryNinja::Architecture* arch, - BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); + PseudoCFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); std::string GetAnnotationStartString() const override; std::string GetAnnotationEndString() const override; diff --git a/lang/rust/pseudorust.cpp b/lang/rust/pseudorust.cpp index a1f7defe..837a7932 100644 --- a/lang/rust/pseudorust.cpp +++ b/lang/rust/pseudorust.cpp @@ -7,9 +7,9 @@ using namespace std; using namespace BinaryNinja; -PseudoRustFunction::PseudoRustFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner, - HighLevelILFunction* highLevelILFunction) : - LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) +PseudoRustFunction::PseudoRustFunction( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : + LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) { } @@ -2814,7 +2814,7 @@ PseudoRustFunctionType::PseudoRustFunctionType(): LanguageRepresentationFunction Ref<LanguageRepresentationFunction> PseudoRustFunctionType::Create(Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) { - return new PseudoRustFunction(this, arch, owner, highLevelILFunction); + return new PseudoRustFunction(arch, owner, highLevelILFunction); } diff --git a/lang/rust/pseudorust.h b/lang/rust/pseudorust.h index 96e656c2..a2ab9584 100644 --- a/lang/rust/pseudorust.h +++ b/lang/rust/pseudorust.h @@ -59,8 +59,7 @@ protected: virtual void EndLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; public: - PseudoRustFunction(BinaryNinja::LanguageRepresentationFunctionType* type, BinaryNinja::Architecture* arch, - BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); + PseudoRustFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); std::string GetAnnotationStartString() const override; std::string GetAnnotationEndString() const override; diff --git a/languagerepresentation.cpp b/languagerepresentation.cpp index 1af38b4b..0930c0b8 100644 --- a/languagerepresentation.cpp +++ b/languagerepresentation.cpp @@ -4,8 +4,7 @@ using namespace BinaryNinja; using namespace std; -LanguageRepresentationFunction::LanguageRepresentationFunction( - LanguageRepresentationFunctionType* type, Architecture* arch, Function* func, HighLevelILFunction* highLevelIL) +LanguageRepresentationFunction::LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL) { BNCustomLanguageRepresentationFunction callbacks; callbacks.context = this; @@ -21,8 +20,8 @@ LanguageRepresentationFunction::LanguageRepresentationFunction( callbacks.getAnnotationStartString = GetAnnotationStartStringCallback; callbacks.getAnnotationEndString = GetAnnotationEndStringCallback; AddRefForRegistration(); - m_object = BNCreateCustomLanguageRepresentationFunction( - type->GetObject(), arch->GetObject(), func->GetObject(), highLevelIL->GetObject(), &callbacks); + m_object = BNCreateCustomLanguageRepresentationFunction(arch->GetObject(), func->GetObject(), + highLevelIL->GetObject(), &callbacks); } @@ -113,12 +112,6 @@ BNHighlightColor LanguageRepresentationFunction::GetHighlight(BasicBlock* block) } -Ref<LanguageRepresentationFunctionType> LanguageRepresentationFunction::GetLanguage() const -{ - return new CoreLanguageRepresentationFunctionType(BNGetLanguageRepresentationType(m_object)); -} - - Ref<Architecture> LanguageRepresentationFunction::GetArchitecture() const { return new CoreArchitecture(BNGetLanguageRepresentationArchitecture(m_object)); @@ -319,7 +312,6 @@ void LanguageRepresentationFunctionType::Register(LanguageRepresentationFunction callbacks.isValid = IsValidCallback; callbacks.getTypePrinter = GetTypePrinterCallback; callbacks.getTypeParser = GetTypeParserCallback; - callbacks.getLineFormatter = GetLineFormatterCallback; callbacks.getFunctionTypeTokens = GetFunctionTypeTokensCallback; callbacks.freeLines = FreeLinesCallback; @@ -371,16 +363,6 @@ BNTypeParser* LanguageRepresentationFunctionType::GetTypeParserCallback(void* ct } -BNLineFormatter* LanguageRepresentationFunctionType::GetLineFormatterCallback(void* ctxt) -{ - LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; - Ref<LineFormatter> result = type->GetLineFormatter(); - if (!result) - return nullptr; - return result->GetObject(); -} - - BNDisassemblyTextLine* LanguageRepresentationFunctionType::GetFunctionTypeTokensCallback( void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count) { @@ -490,15 +472,6 @@ Ref<TypeParser> CoreLanguageRepresentationFunctionType::GetTypeParser() } -Ref<LineFormatter> CoreLanguageRepresentationFunctionType::GetLineFormatter() -{ - BNLineFormatter* formatter = BNGetLanguageRepresentationFunctionTypeLineFormatter(m_object); - if (!formatter) - return nullptr; - return new CoreLineFormatter(formatter); -} - - vector<DisassemblyTextLine> CoreLanguageRepresentationFunctionType::GetFunctionTypeTokens( Function* func, DisassemblySettings* settings) { diff --git a/lineformatter.cpp b/lineformatter.cpp deleted file mode 100644 index 71da6b6e..00000000 --- a/lineformatter.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2024 Vector 35 Inc -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -#include "binaryninjaapi.h" - -using namespace BinaryNinja; -using namespace std; - - -LineFormatterSettings LineFormatterSettings::GetDefault(DisassemblySettings* settings, HighLevelILFunction* func) -{ - BNLineFormatterSettings* apiObj = - BNGetDefaultLineFormatterSettings(settings ? settings->GetObject() : nullptr, func->GetObject()); - LineFormatterSettings result = FromAPIObject(apiObj); - BNFreeLineFormatterSettings(apiObj); - return result; -} - - -LineFormatterSettings LineFormatterSettings::GetLanguageRepresentationSettings( - DisassemblySettings* settings, LanguageRepresentationFunction* func) -{ - BNLineFormatterSettings* apiObj = - BNGetLanguageRepresentationLineFormatterSettings(settings ? settings->GetObject() : nullptr, func->GetObject()); - LineFormatterSettings result = FromAPIObject(apiObj); - BNFreeLineFormatterSettings(apiObj); - return result; -} - - -LineFormatterSettings LineFormatterSettings::FromAPIObject(const BNLineFormatterSettings* settings) -{ - LineFormatterSettings result; - result.highLevelIL = new HighLevelILFunction(BNNewHighLevelILFunctionReference(settings->highLevelIL)); - result.desiredLineLength = settings->desiredLineLength; - result.minimumContentLength = settings->minimumContentLength; - result.tabWidth = settings->tabWidth; - result.languageName = settings->languageName; - result.commentStartString = settings->commentStartString; - result.commentEndString = settings->commentEndString; - result.annotationStartString = settings->annotationStartString; - result.annotationEndString = settings->annotationEndString; - return result; -} - - -BNLineFormatterSettings LineFormatterSettings::ToAPIObject() const -{ - BNLineFormatterSettings result; - result.highLevelIL = highLevelIL->GetObject(); - result.desiredLineLength = desiredLineLength; - result.minimumContentLength = minimumContentLength; - result.tabWidth = tabWidth; - result.languageName = (char*)languageName.c_str(); - result.commentStartString = (char*)commentStartString.c_str(); - result.commentEndString = (char*)commentEndString.c_str(); - result.annotationStartString = (char*)annotationStartString.c_str(); - result.annotationEndString = (char*)annotationEndString.c_str(); - return result; -} - - -LineFormatter::LineFormatter(const string& name) : m_nameForRegister(name) {} - - -LineFormatter::LineFormatter(BNLineFormatter* formatter) -{ - m_object = formatter; -} - - -void LineFormatter::Register(LineFormatter* formatter) -{ - BNCustomLineFormatter cb; - cb.context = formatter; - cb.formatLines = FormatLinesCallback; - cb.freeLines = FreeLinesCallback; - - formatter->AddRefForRegistration(); - formatter->m_object = BNRegisterLineFormatter(formatter->m_nameForRegister.c_str(), &cb); -} - - -BNDisassemblyTextLine* LineFormatter::FormatLinesCallback(void* ctxt, BNDisassemblyTextLine* inLines, size_t inCount, - const BNLineFormatterSettings* settings, size_t* outCount) -{ - LineFormatter* formatter = (LineFormatter*)ctxt; - - vector<DisassemblyTextLine> input; - input.reserve(inCount); - for (size_t i = 0; i < inCount; i++) - { - DisassemblyTextLine line; - line.addr = inLines[i].addr; - line.instrIndex = inLines[i].instrIndex; - line.highlight = inLines[i].highlight; - line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(inLines[i].tokens, inLines[i].count); - line.tags = Tag::ConvertTagList(inLines[i].tags, inLines[i].tagCount); - input.push_back(line); - } - - vector<DisassemblyTextLine> outLines = - formatter->FormatLines(input, LineFormatterSettings::FromAPIObject(settings)); - - *outCount = outLines.size(); - BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[outLines.size()]; - for (size_t i = 0; i < outLines.size(); i++) - { - const DisassemblyTextLine& line = outLines[i]; - buf[i].addr = line.addr; - buf[i].instrIndex = line.instrIndex; - buf[i].highlight = line.highlight; - buf[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(line.tokens); - buf[i].count = line.tokens.size(); - buf[i].tags = Tag::CreateTagList(line.tags, &(buf[i].tagCount)); - } - - return buf; -} - - -void LineFormatter::FreeLinesCallback(void*, BNDisassemblyTextLine* lines, size_t count) -{ - for (size_t i = 0; i < count; i++) - { - InstructionTextToken::FreeInstructionTextTokenList(lines[i].tokens, lines[i].count); - Tag::FreeTagList(lines[i].tags, lines[i].tagCount); - } - delete[] lines; -} - - -vector<Ref<LineFormatter>> LineFormatter::GetList() -{ - size_t count; - BNLineFormatter** list = BNGetLineFormatterList(&count); - vector<Ref<LineFormatter>> result; - for (size_t i = 0; i < count; i++) - result.push_back(new CoreLineFormatter(list[i])); - BNFreeLineFormatterList(list); - return result; -} - - -Ref<LineFormatter> LineFormatter::GetByName(const string& name) -{ - BNLineFormatter* result = BNGetLineFormatterByName(name.c_str()); - if (!result) - return nullptr; - return new CoreLineFormatter(result); -} - - -Ref<LineFormatter> LineFormatter::GetDefault() -{ - BNLineFormatter* result = BNGetDefaultLineFormatter(); - if (!result) - return nullptr; - return new CoreLineFormatter(result); -} - - -CoreLineFormatter::CoreLineFormatter(BNLineFormatter* formatter) : LineFormatter(formatter) {} - - -vector<DisassemblyTextLine> CoreLineFormatter::FormatLines( - const vector<DisassemblyTextLine>& lines, const LineFormatterSettings& settings) -{ - size_t inCount = lines.size(); - BNDisassemblyTextLine* inLines = new BNDisassemblyTextLine[lines.size()]; - for (size_t i = 0; i < lines.size(); i++) - { - const DisassemblyTextLine& line = lines[i]; - inLines[i].addr = line.addr; - inLines[i].instrIndex = line.instrIndex; - inLines[i].highlight = line.highlight; - inLines[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(line.tokens); - inLines[i].count = line.tokens.size(); - inLines[i].tags = Tag::CreateTagList(line.tags, &(inLines[i].tagCount)); - } - - size_t outCount = 0; - BNLineFormatterSettings apiSettings = settings.ToAPIObject(); - BNDisassemblyTextLine* outLines = BNFormatLines(m_object, inLines, inCount, &apiSettings, &outCount); - - for (size_t i = 0; i < inCount; i++) - { - InstructionTextToken::FreeInstructionTextTokenList(inLines[i].tokens, inLines[i].count); - Tag::FreeTagList(inLines[i].tags, inLines[i].tagCount); - } - delete[] inLines; - - vector<DisassemblyTextLine> result; - result.reserve(outCount); - for (size_t i = 0; i < outCount; i++) - { - DisassemblyTextLine line; - line.addr = outLines[i].addr; - line.instrIndex = outLines[i].instrIndex; - line.highlight = outLines[i].highlight; - line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(outLines[i].tokens, outLines[i].count); - line.tags = Tag::ConvertTagList(outLines[i].tags, outLines[i].tagCount); - result.push_back(line); - } - - BNFreeDisassemblyTextLines(outLines, outCount); - return result; -} diff --git a/python/__init__.py b/python/__init__.py index 1cdc541e..9b5a394e 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -80,7 +80,6 @@ from .externallibrary import * from .undo import * from .fileaccessor import * from .languagerepresentation import * -from .lineformatter import * # We import each of these by name to prevent conflicts between # log.py and the function 'log' which we don't import below from .log import ( diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py index 655039fe..6a9e401b 100644 --- a/python/examples/pseudo_python.py +++ b/python/examples/pseudo_python.py @@ -316,7 +316,16 @@ class PseudoPythonFunction(LanguageRepresentationFunction): tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) tokens.append(instr.var.type.get_tokens()) elif instr.operation == HighLevelILOperation.HLIL_FLOAT_CONST: - tokens.append(InstructionTextToken(InstructionTextTokenType.FloatingPointToken, f"{instr.constant:g}")) + # The constant value in the instruction contains the raw bits of the floating point value. Convert + # this to a floating point value and display it. + if instr.size == 4: + value = struct.unpack("<f", struct.pack("<I", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + elif instr.size == 8: + value = struct.unpack("<d", struct.pack("<Q", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + else: + tokens.append_integer_text_token(instr, instr.constant, instr.size) elif instr.operation == HighLevelILOperation.HLIL_CONST: # Check for bool type. Display these as True or False. The default handling will use C style # booleans instead of Python style. @@ -1066,7 +1075,7 @@ class PseudoPythonFunctionType(LanguageRepresentationFunctionType): language_name = "Pseudo Python" def create(self, arch: Architecture, owner: Function, hlil: HighLevelILFunction): - return PseudoPythonFunction(self, arch, owner, hlil) + return PseudoPythonFunction(arch, owner, hlil) def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> DisassemblyTextLine: tokens = [] diff --git a/python/function.py b/python/function.py index 0fc60ffc..e962d91f 100644 --- a/python/function.py +++ b/python/function.py @@ -135,18 +135,6 @@ class DisassemblySettings: option = DisassemblyOption[option] core.BNSetDisassemblySettingsOption(self.handle, option, state) - @staticmethod - def default_settings() -> 'DisassemblySettings': - return DisassemblySettings(core.BNDefaultDisassemblySettings()) - - @staticmethod - def default_graph_settings() -> 'DisassemblySettings': - return DisassemblySettings(core.BNDefaultGraphDisassemblySettings()) - - @staticmethod - def default_linear_settings() -> 'DisassemblySettings': - return DisassemblySettings(core.BNDefaultLinearDisassemblySettings()) - @dataclass class ILReferenceSource: diff --git a/python/highlevelil.py b/python/highlevelil.py index 85be4794..1d7feb71 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -26,10 +26,7 @@ from enum import Enum # Binary Ninja components from . import _binaryninjacore as core -from .enums import ( - HighLevelILOperation, DataFlowQueryOption, FunctionGraphType, ILInstructionAttribute, StringType, - DisassemblyOption -) +from .enums import HighLevelILOperation, DataFlowQueryOption, FunctionGraphType, ILInstructionAttribute, StringType from . import function from . import binaryview from . import architecture @@ -334,9 +331,7 @@ class HighLevelILInstruction(BaseILInstruction): return ILInstruction[instr.operation](func, expr_index, core_instr, as_ast, instr_index) def __str__(self): - settings = function.DisassemblySettings.default_settings() - settings.set_option(DisassemblyOption.DisableLineFormatting) - lines = self.get_lines(settings) + lines = self.lines if lines is None: return "invalid" result = [] @@ -348,9 +343,7 @@ class HighLevelILInstruction(BaseILInstruction): return '\n'.join(result) def __repr__(self): - settings = function.DisassemblySettings.default_settings() - settings.set_option(DisassemblyOption.DisableLineFormatting) - lines = self.get_lines(settings) + lines = self.lines continuation = "" if lines is None: first_line = "<invalid>" @@ -393,14 +386,26 @@ class HighLevelILInstruction(BaseILInstruction): @property def tokens(self) -> TokenList: """HLIL tokens taken from the HLIL text lines (read-only) -- does not include newlines or indentation, use lines for that information""" - settings = function.DisassemblySettings.default_settings() - settings.set_option(DisassemblyOption.DisableLineFormatting) - return [token for line in self.get_lines(settings) for token in line.tokens] + return [token for line in self.lines for token in line.tokens] @property def lines(self) -> LinesType: """HLIL text lines (read-only)""" - return self.get_lines() + count = ctypes.c_ulonglong() + lines = core.BNGetHighLevelILExprText(self.function.handle, self.expr_index, self.as_ast, count, None) + assert lines is not None, "core.BNGetHighLevelILExprText returned None" + try: + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = self.function[lines[i].instrIndex] + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + yield function.DisassemblyTextLine(tokens, addr, il_instr, color) + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) @property def prefix_operands(self) -> List[Union[HighLevelILOperandType, HighLevelILOperationAndSize]]: @@ -913,26 +918,6 @@ class HighLevelILInstruction(BaseILInstruction): def has_side_effects(self) -> bool: return core.BNHighLevelILHasSideEffects(self.function.handle, self.expr_index) - def get_lines(self, settings: Optional['function.DisassemblySettings'] = None) -> LinesType: - """Gets HLIL text lines with optional settings""" - if settings is not None: - settings = settings.handle - count = ctypes.c_ulonglong() - lines = core.BNGetHighLevelILExprText(self.function.handle, self.expr_index, self.as_ast, count, settings) - assert lines is not None, "core.BNGetHighLevelILExprText returned None" - try: - for i in range(0, count.value): - addr = lines[i].addr - if lines[i].instrIndex != 0xffffffffffffffff: - il_instr = self.function[lines[i].instrIndex] - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) - yield function.DisassemblyTextLine(tokens, addr, il_instr, color) - finally: - core.BNFreeDisassemblyTextLines(lines, count.value) - @dataclass(frozen=True, repr=False, eq=False) class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation): diff --git a/python/languagerepresentation.py b/python/languagerepresentation.py index a0a5a234..ac34c860 100644 --- a/python/languagerepresentation.py +++ b/python/languagerepresentation.py @@ -30,7 +30,6 @@ from . import binaryview from . import function from . import highlevelil from . import highlight -from . import lineformatter from . import variable from . import types from .log import log_error @@ -328,8 +327,7 @@ class LanguageRepresentationFunction: annotation_end_string = "}" def __init__( - self, func_type: Optional['LanguageRepresentationFunctionType'] = None, - arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None, + self, arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None, hlil: Optional['highlevelil.HighLevelILFunction'] = None, handle=None ): if handle is None: @@ -356,9 +354,7 @@ class LanguageRepresentationFunction: self.comment_end_string = self.__class__.comment_end_string self.annotation_start_string = self.__class__.annotation_start_string self.annotation_end_string = self.__class__.annotation_end_string - _handle = core.BNCreateCustomLanguageRepresentationFunction( - func_type.handle, arch.handle, owner.handle, hlil.handle, self._cb - ) + _handle = core.BNCreateCustomLanguageRepresentationFunction(arch.handle, owner.handle, hlil.handle, self._cb) assert _handle is not None else: self.comment_start_string = core.BNGetLanguageRepresentationFunctionCommentStartString(handle) @@ -625,7 +621,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti self._cb.isValid = self._cb.isValid.__class__(self._is_valid) self._cb.getTypePrinter = self._cb.getTypePrinter.__class__(self._type_printer) self._cb.getTypeParser = self._cb.getTypeParser.__class__(self._type_parser) - self._cb.getLineFormatter = self._cb.getLineFormatter.__class__(self._line_formatter) self._cb.getFunctionTypeTokens = self._cb.getFunctionTypeTokens.__class__(self._function_type_tokens) self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) self.handle = core.BNRegisterLanguageRepresentationFunctionType(self.__class__.language_name, self._cb) @@ -677,16 +672,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti log_error(traceback.format_exc()) return None - def _line_formatter(self, ctxt): - try: - result = self.line_formatter - if result is None: - return None - return ctypes.cast(result.handle, ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return None - def _function_type_tokens( self, ctxt, func: core.BNFunctionHandle, settings: Optional[core.BNDisassemblySettingsHandle], count: ctypes.POINTER(ctypes.c_ulonglong) @@ -766,14 +751,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti """ return None - @property - def line_formatter(self) -> Optional['lineformatter.LineFormatter']: - """ - Returns the line formatter for formatting lines in this language. If ``None`` is returned, the default - line formatter will be used. - """ - return None - def function_type_tokens( self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] ) -> List['function.DisassemblyTextLine']: @@ -829,13 +806,6 @@ class CoreLanguageRepresentationFunctionType(LanguageRepresentationFunctionType) return None return binaryninja.typeparser.TypeParser(handle=result) - @property - def line_formatter(self) -> Optional['lineformatter.LineFormatter']: - result = core.BNGetLanguageRepresentationFunctionTypeLineFormatter(self.handle) - if result is None: - return None - return binaryninja.lineformatter.LineFormatter(handle=result) - def function_type_tokens( self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] ) -> List['function.DisassemblyTextLine']: diff --git a/python/lineformatter.py b/python/lineformatter.py deleted file mode 100644 index 7f74572b..00000000 --- a/python/lineformatter.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) 2025 Vector 35 Inc -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -import ctypes -import traceback -from dataclasses import dataclass -from typing import List, Optional, Union - -# Binary Ninja components -import binaryninja -from . import _binaryninjacore as core -from . import function -from . import highlevelil -from . import highlight -from . import languagerepresentation -from .log import log_error -from .enums import HighlightStandardColor - - -@dataclass(frozen=True) -class LineFormatterSettings: - hlil: highlevelil.HighLevelILFunction - desired_line_length: int - minimum_content_length: int - tab_width: int - language_name: Optional[str] - comment_start_string: str - comment_end_string: str - annotation_start_string: str - annotation_end_string: str - - @staticmethod - def default(settings: Optional['function.DisassemblySettings'], hlil: 'highlevelil.HighLevelILFunction') -> 'LineFormatterSettings': - """ - Gets the default line formatter settings for High Level IL code. - """ - if settings is not None: - settings = settings.handle - api_obj = core.BNGetDefaultLineFormatterSettings(settings, hlil.handle) - result = LineFormatterSettings._from_core_struct(api_obj[0]) - core.BNFreeLineFormatterSettings(api_obj) - return result - - @staticmethod - def language_representation_settings( - settings: Optional['function.DisassemblySettings'], func: 'languagerepresentation.LanguageRepresentationFunction' - ) -> 'LineFormatterSettings': - """ - Gets the default line formatter settings for a language representation function. - """ - if settings is not None: - settings = settings.handle - api_obj = core.BNGetLanguageRepresentationLineFormatterSettings(settings, func.handle) - result = LineFormatterSettings._from_core_struct(api_obj[0]) - core.BNFreeLineFormatterSettings(api_obj) - return result - - @staticmethod - def _from_core_struct(settings: core.BNLineFormatterSettings) -> 'LineFormatterSettings': - if len(settings.languageName) == 0: - language_name = None - else: - language_name = settings.languageName - hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(settings.highLevelIL)) - return LineFormatterSettings( - hlil, settings.desiredLineLength, settings.minimumContentLength, settings.tabWidth, language_name, - settings.commentStartString, settings.commentEndString, - settings.annotationStartString, settings.annotationEndString - ) - - def _to_core_struct(self) -> core.BNLineFormatterSettings: - result = core.BNLineFormatterSettings() - result.highLevelIL = self.hlil.handle - result.desiredLineLength = self.desired_line_length - result.minimumContentLength = self.minimum_content_length - result.tabWidth = self.tab_width - result.languageName = self.language_name if self.language_name is not None else "" - result.commentStartString = self.comment_start_string - result.commentEndString = self.comment_end_string - result.annotationStartString = self.annotation_start_string - result.annotationEndString = self.annotation_end_string - return result - - -class _LineFormatterMetaClass(type): - def __iter__(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetLineFormatterList(count) - assert types is not None, "core.BNGetLineFormatterList returned None" - try: - for i in range(0, count.value): - yield CoreLineFormatter(handle=types[i]) - finally: - core.BNFreeLineFormatterList(types) - - def __getitem__(cls, value): - binaryninja._init_plugins() - lang = core.BNGetLineFormatterByName(str(value)) - if lang is None: - raise KeyError("'%s' is not a valid formatter" % str(value)) - return CoreLineFormatter(handle=lang) - - -class LineFormatter(metaclass=_LineFormatterMetaClass): - """ - ``class LineFormatter`` represents a custom line formatter, which can reformat code in High Level IL - and high level language representations. - """ - _registered_formatters = [] - formatter_name = None - - def __init__(self, handle=None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNLineFormatter) - - def register(self): - """Registers the line formatter.""" - if self.__class__.formatter_name is None: - raise ValueError("formatter_name is missing") - self._cb = core.BNCustomLineFormatter() - self._cb.context = 0 - self._cb.formatLines = self._cb.formatLines.__class__(self._format_lines) - self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) - self.handle = core.BNRegisterLineFormatter(self.__class__.formatter_name, self._cb) - self.__class__._registered_formatters.append(self) - - def _format_lines( - self, ctxt, in_lines, in_count: int, settings: core.BNLineFormatterSettingsHandle, - out_count: ctypes.POINTER(ctypes.c_ulonglong) - ): - try: - settings = settings[0] - if len(settings.languageName) == 0: - language_name = None - else: - language_name = settings.languageName - hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(settings.highLevelIL)) - settings = LineFormatterSettings( - hlil, settings.desiredLineLength, settings.minimumContentLength, settings.tabWidth, language_name, - settings.commentStartString, settings.commentEndString, - settings.annotationStartString, settings.annotationEndString - ) - - lines = [] - if in_lines is not None: - for i in range(0, in_count): - addr = in_lines[i].addr - if in_lines[i].instrIndex != 0xffffffffffffffff: - il_instr = hlil[in_lines[i].instrIndex] # type: ignore - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(in_lines[i].highlight) - tokens = function.InstructionTextToken._from_core_struct(in_lines[i].tokens, in_lines[i].count) - lines.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) - - lines = self.format_lines(lines, settings) - - out_count[0] = len(lines) - self.line_buf = (core.BNDisassemblyTextLine * len(lines))() - for i in range(len(lines)): - line = lines[i] - color = line.highlight - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") - if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) - self.line_buf[i].highlight = color._to_core_struct() - if line.address is None: - if len(line.tokens) > 0: - self.line_buf[i].addr = line.tokens[0].address - else: - self.line_buf[i].addr = 0 - else: - self.line_buf[i].addr = line.address - if line.il_instruction is not None: - self.line_buf[i].instrIndex = line.il_instruction.instr_index - else: - self.line_buf[i].instrIndex = 0xffffffffffffffff - - self.line_buf[i].count = len(line.tokens) - self.line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) - - return ctypes.cast(self.line_buf, ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - out_count[0] = 0 - return None - - def _free_lines(self, ctxt, lines, count): - self.line_buf = None - - def format_lines( - self, in_lines: List['function.DisassemblyTextLine'], settings: 'LineFormatterSettings' - ) -> List['function.DisassemblyTextLine']: - """ - Reformats the given list of lines. Returns a new list of lines containing the reformatted code. - """ - raise NotImplementedError - - @property - def name(self) -> str: - if hasattr(self, 'handle'): - return core.BNGetLineFormatterName(self.handle) - return self.__class__.formatter_name - - def __repr__(self): - return f"<LineFormatter: {self.name}>" - - -_formatter_cache = {} - - -class CoreLineFormatter(LineFormatter): - def __init__(self, handle: core.BNLineFormatter): - super(CoreLineFormatter, self).__init__(handle=handle) - if type(self) is CoreLineFormatter: - global _formatter_cache - _formatter_cache[ctypes.addressof(handle.contents)] = self - - def format_lines( - self, in_lines: List['function.DisassemblyTextLine'], settings: 'LineFormatterSettings' - ) -> List['function.DisassemblyTextLine']: - line_buf = (core.BNDisassemblyTextLine * len(in_lines))() - for i in range(len(in_lines)): - line = in_lines[i] - color = line.highlight - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") - if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) - line_buf[i].highlight = color._to_core_struct() - if line.address is None: - if len(line.tokens) > 0: - line_buf[i].addr = line.tokens[0].address - else: - line_buf[i].addr = 0 - else: - line_buf[i].addr = line.address - if line.il_instruction is not None: - line_buf[i].instrIndex = line.il_instruction.instr_index - else: - line_buf[i].instrIndex = 0xffffffffffffffff - - line_buf[i].count = len(line.tokens) - line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) - - count = ctypes.c_ulonglong() - lines = core.BNFormatLines(self.handle, line_buf, len(in_lines), settings._to_core_struct(), count) - - result = [] - if lines is not None: - result = [] - for i in range(0, count.value): - addr = lines[i].addr - if lines[i].instrIndex != 0xffffffffffffffff: - il_instr = settings.hlil[lines[i].instrIndex] # type: ignore - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) - result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result - - @classmethod - def _from_cache(cls, handle) -> 'LineFormatter': - """ - Look up a representation type from a given BNLineFormatter handle - :param handle: BNLineFormatter pointer - :return: Formatter instance responsible for this handle - """ - global _formatter_cache - return _formatter_cache.get(ctypes.addressof(handle.contents)) or cls(handle) |
