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 /formatter/generic | |
| parent | cbd4d7f12d54ddc4b6d3d90a8d7b49591f468a94 (diff) | |
Revert "Add line formatter API and a generic line formatter plugin"
This reverts commit 1699c71999d29d32aba5c9f8fea193a661a4b02b.
Diffstat (limited to 'formatter/generic')
| -rw-r--r-- | formatter/generic/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | formatter/generic/genericformatter.cpp | 895 | ||||
| -rw-r--r-- | formatter/generic/genericformatter.h | 14 |
3 files changed, 0 insertions, 954 deletions
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; -}; |
