diff --git a/common/goos/CMakeLists.txt b/common/goos/CMakeLists.txt index 4347c0d3a1..2c36a8302f 100644 --- a/common/goos/CMakeLists.txt +++ b/common/goos/CMakeLists.txt @@ -1,2 +1,2 @@ -add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp) +add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp PrettyPrinter.cpp) target_link_libraries(goos common_util fmt) \ No newline at end of file diff --git a/common/goos/Object.cpp b/common/goos/Object.cpp index 99b66d5dca..69ee9d22b1 100644 --- a/common/goos/Object.cpp +++ b/common/goos/Object.cpp @@ -4,6 +4,9 @@ namespace goos { std::shared_ptr gEmptyList = nullptr; +std::shared_ptr& get_empty_list() { + return gEmptyList; +} /*! * Convert type to string (name in brackets) diff --git a/common/goos/Object.h b/common/goos/Object.h index a9bd7796bd..8e50ae9e97 100644 --- a/common/goos/Object.h +++ b/common/goos/Object.h @@ -319,7 +319,7 @@ class Object { // There is a single heap allocated EmptyListObject. class EmptyListObject; -extern std::shared_ptr gEmptyList; +std::shared_ptr& get_empty_list(); class EmptyListObject : public HeapObject { public: @@ -327,10 +327,10 @@ class EmptyListObject : public HeapObject { static Object make_new() { Object obj; obj.type = ObjectType::EMPTY_LIST; - if (!gEmptyList) { - gEmptyList = std::make_shared(); + if (!get_empty_list()) { + get_empty_list() = std::make_shared(); } - obj.heap_obj = gEmptyList; + obj.heap_obj = get_empty_list(); return obj; } diff --git a/common/goos/PrettyPrinter.cpp b/common/goos/PrettyPrinter.cpp new file mode 100644 index 0000000000..6bae9a4b59 --- /dev/null +++ b/common/goos/PrettyPrinter.cpp @@ -0,0 +1,567 @@ +#include +#include +#include +#include "PrettyPrinter.h" +#include "Reader.h" + +namespace pretty_print { + +/*! + * A single token which cannot be split between lines. + */ +struct FormToken { + enum class TokenKind { + WHITESPACE, + STRING, + OPEN_PAREN, + DOT, + CLOSE_PAREN, + EMPTY_PAIR, + SPECIAL_STRING // has different alignment rules than STRING + } kind; + explicit FormToken(TokenKind _kind, std::string _str = "") : kind(_kind), str(std::move(_str)) {} + + std::string str; + + std::string toString() const { + std::string s; + switch (kind) { + case TokenKind::WHITESPACE: + s.push_back(' '); + break; + case TokenKind::STRING: + s.append(str); + break; + case TokenKind::OPEN_PAREN: + s.push_back('('); + break; + case TokenKind::DOT: + s.push_back('.'); + break; + case TokenKind::CLOSE_PAREN: + s.push_back(')'); + break; + case TokenKind::EMPTY_PAIR: + s.append("()"); + break; + case TokenKind::SPECIAL_STRING: + s.append(str); + break; + default: + throw std::runtime_error("toString unknown token kind"); + } + return s; + } +}; + +/*! + * Convert a GOOS object to tokens and add it to the list. + * This is the main function which recursively builds a list of tokens out of an s-expression. + * + * Note that not all GOOS objects can be pretty printed. Only the ones that can be directly + * generated by the reader. + */ +void add_to_token_list(const goos::Object& obj, std::vector* tokens) { + switch (obj.type) { + case goos::ObjectType::EMPTY_LIST: + tokens->emplace_back(FormToken::TokenKind::EMPTY_PAIR); + break; + // all of these can just be printed to a string and turned into a 'symbol' + case goos::ObjectType::INTEGER: + case goos::ObjectType::FLOAT: + case goos::ObjectType::CHAR: + case goos::ObjectType::SYMBOL: + case goos::ObjectType::STRING: + tokens->emplace_back(FormToken::TokenKind::STRING, obj.print()); + break; + + // it's important to break the pair up into smaller tokens which can then be split + // across lines. + case goos::ObjectType::PAIR: { + tokens->emplace_back(FormToken::TokenKind::OPEN_PAREN); + auto* to_print = &obj; + for (;;) { + if (to_print->is_pair()) { + // first print the car into our token list: + add_to_token_list(to_print->as_pair()->car, tokens); + // then load up the cdr as the next thing to print + to_print = &to_print->as_pair()->cdr; + if (to_print->is_empty_list()) { + // we're done, add a close paren and finish + tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN); + return; + } else { + // more to print, add whitespace + tokens->emplace_back(FormToken::TokenKind::WHITESPACE); + } + } else { + // got an improper list. + // add a dot, space + tokens->emplace_back(FormToken::TokenKind::DOT); + tokens->emplace_back(FormToken::TokenKind::WHITESPACE); + // then the thing and a close paren. + add_to_token_list(*to_print, tokens); + tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN); + return; // and we're done with this list. + } + } + } break; + + // these are unsupported by the pretty printer. + case goos::ObjectType::ARRAY: // todo, we should probably handle arrays. + case goos::ObjectType::LAMBDA: + case goos::ObjectType::MACRO: + case goos::ObjectType::ENVIRONMENT: + throw std::runtime_error("tried to pretty print a goos object kind which is not supported."); + default: + assert(false); + } +} + +/*! + * Linked list node representing a token in the output (whitespace, paren, newline, etc) + */ +struct PrettyPrinterNode { + FormToken* tok = nullptr; // if we aren't a newline, we will have a token. + int line = -1; // line that token occurs on. undef for newlines + int lineIndent = -1; // indent of line. only valid for first token in the line + int offset = -1; // offset of beginning of token from left margin + int specialIndentDelta = 0; + bool is_line_separator = false; // true if line separator (not a token) + PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list + PrettyPrinterNode* paren = + nullptr; // pointer to open paren if in parens. open paren points to close and vice versa + explicit PrettyPrinterNode(FormToken* _tok) { tok = _tok; } + PrettyPrinterNode() = default; +}; + +/*! + * Container to track and cleanup all nodes after use. + */ +struct NodePool { + std::vector nodes; + PrettyPrinterNode* allocate(FormToken* x) { + auto result = new PrettyPrinterNode(x); + nodes.push_back(result); + return result; + } + + PrettyPrinterNode* allocate() { + auto result = new PrettyPrinterNode; + nodes.push_back(result); + return result; + } + + NodePool() = default; + + ~NodePool() { + for (auto& x : nodes) { + delete x; + } + } + + // so we don't accidentally copy this. + NodePool& operator=(const NodePool&) = delete; + NodePool(const NodePool&) = delete; +}; + +/*! + * Splice in a line break after the given node, it there isn't one already and if it isn't the last + * node. + */ +void insertNewlineAfter(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) { + if (node->next && !node->next->is_line_separator) { + auto* nl = pool.allocate(); + auto* next = node->next; + node->next = nl; + nl->prev = node; + nl->next = next; + next->prev = nl; + nl->is_line_separator = true; + nl->specialIndentDelta = specialIndentDelta; + } +} + +/*! + * Splice in a line break before the given node, if there isn't one already and if it isn't the + * first node. + */ +void insertNewlineBefore(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) { + if (node->prev && !node->prev->is_line_separator) { + auto* nl = pool.allocate(); + auto* prev = node->prev; + prev->next = nl; + nl->prev = prev; + nl->next = node; + node->prev = nl; + nl->is_line_separator = true; + nl->specialIndentDelta = specialIndentDelta; + } +} + +/*! + * Break a list across multiple lines. This is how line lengths are decreased. + * This does not compute the proper indentation and leaves the list in a bad state. + * After this has been called, the entire selection should be reformatted with propagate_pretty + */ +void breakList(NodePool& pool, PrettyPrinterNode* leftParen) { + assert(!leftParen->is_line_separator); + assert(leftParen->tok->kind == FormToken::TokenKind::OPEN_PAREN); + auto* rp = leftParen->paren; + assert(rp->tok->kind == FormToken::TokenKind::CLOSE_PAREN); + + for (auto* n = leftParen->next; n && n != rp; n = n->next) { + if (!n->is_line_separator) { + if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) { + n = n->paren; + assert(n->tok->kind == FormToken::TokenKind::CLOSE_PAREN); + insertNewlineAfter(pool, n, 0); + } else if (n->tok->kind != FormToken::TokenKind::WHITESPACE) { + assert(n->tok->kind != FormToken::TokenKind::CLOSE_PAREN); + insertNewlineAfter(pool, n, 0); + } + } + } +} + +/*! + * Compute proper line numbers, offsets, and indents for a list of tokens with newlines + * Will add newlines for close parens if needed. + */ +static PrettyPrinterNode* propagatePretty(NodePool& pool, + PrettyPrinterNode* list, + int line_length) { + // propagate line numbers + PrettyPrinterNode* rv = nullptr; + int line = list->line; + for (auto* n = list; n; n = n->next) { + if (n->is_line_separator) { + line++; + } else { + n->line = line; + // add the weird newline. + if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) { + if (n->line != n->paren->line) { + if (n->prev && !n->prev->is_line_separator) { + insertNewlineBefore(pool, n, 0); + line++; + } + if (n->next && !n->next->is_line_separator) { + insertNewlineAfter(pool, n, 0); + } + } + } + } + } + + // compute offsets and indents + std::vector indentStack; + indentStack.push_back(0); + int offset = 0; + PrettyPrinterNode* line_start = list; + bool previous_line_sep = false; + for (auto* n = list; n; n = n->next) { + if (n->is_line_separator) { + previous_line_sep = true; + offset = indentStack.back() += n->specialIndentDelta; + } else { + if (previous_line_sep) { + line_start = n; + n->lineIndent = offset; + previous_line_sep = false; + } + + n->offset = offset; + offset += n->tok->toString().length(); + if (offset > line_length && !rv) + rv = line_start; + if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) { + if (!n->prev || n->prev->is_line_separator) { + indentStack.push_back(offset + 1); + } else { + indentStack.push_back(offset - 1); + } + } + + if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) { + indentStack.pop_back(); + } + } + } + return rv; +} + +/*! + * Get the token on the start of the next line. nullptr if we're the last line. + */ +PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) { + assert(!start->is_line_separator); + int line = start->line; + for (;;) { + if (start->is_line_separator || start->line == line) { + if (start->next) + start = start->next; + else + return nullptr; + } else { + break; + } + } + return start; +} + +/*! + * Get the next open paren on the current line (can start in the middle of line, not inclusive of + * start) nullptr if there's no open parens on the rest of this line. + */ +PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) { + int line = start->line; + assert(!start->is_line_separator); + if (!start->next || start->next->is_line_separator) + return nullptr; + start = start->next; + while (!start->is_line_separator && start->line == line) { + if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN) + return start; + if (!start->next) + return nullptr; + start = start->next; + } + return nullptr; +} + +/*! + * Get the first open paren on the current line (can start in the middle of line, inclusive of + * start) nullptr if there's no open parens on the rest of this line + */ +PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) { + int line = start->line; + assert(!start->is_line_separator); + while (!start->is_line_separator && start->line == line) { + if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN) + return start; + if (!start->next) + return nullptr; + start = start->next; + } + return nullptr; +} + +/*! + * Get the first token on the first line which exceeds the max length + */ +PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) { + assert(!start->is_line_separator); + int currentLine = start->line; + auto* currentLineNode = start; + for (;;) { + if (start->is_line_separator) { + assert(start->next); + start = start->next; + } else { + if (start->line != currentLine) { + currentLine = start->line; + currentLineNode = start; + } + if (start->offset > line_length) { + return currentLineNode; + } + if (!start->next) { + return nullptr; + } + start = start->next; + } + } +} + +/*! + * Break insertion algorithm. + */ +void insertBreaksAsNeeded(NodePool& pool, PrettyPrinterNode* head, int line_length) { + PrettyPrinterNode* last_line_complete = nullptr; + PrettyPrinterNode* line_to_start_line_search = head; + + // loop over lines + for (;;) { + // compute lines as needed + propagatePretty(pool, head, line_length); + + // search for a bad line starting at the last line we fixed + PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length); + // if we got the same line we started on, this means we couldn't fix it. + if (candidate_line == last_line_complete) { + candidate_line = nullptr; // so we say our candidate was bad and try to find another + PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search); + if (next_line) { + candidate_line = getFirstBadLine(next_line, line_length); + } + } + if (!candidate_line) + break; + + // okay, we have a line which needs fixing. + assert(!candidate_line->prev || candidate_line->prev->is_line_separator); + PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line); + for (;;) { + if (!form_to_start) { + // this means we failed to hit the desired line length... + break; + } + breakList(pool, form_to_start); + propagatePretty(pool, head, line_length); + if (getFirstBadLine(candidate_line, line_length) != candidate_line) { + break; + } + + form_to_start = getNextListOnLine(form_to_start); + if (!form_to_start) + break; + } + + last_line_complete = candidate_line; + line_to_start_line_search = candidate_line; + } +} + +void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) { + for (; node; node = node->next) { + if (!node->is_line_separator && node->tok->kind == FormToken::TokenKind::STRING) { + std::string& name = node->tok->str; + if (name == "deftype") { // todo! + auto* parent_type_dec = getNextListOnLine(node); + if (parent_type_dec) { + insertNewlineAfter(pool, parent_type_dec->paren, 0); + } + } + + if (name == "defun" || name == "defmethod") { + auto* parent_type_dec = getNextListOnLine(node); + if (parent_type_dec) { + insertNewlineAfter(pool, parent_type_dec->paren, 0); + } + } + + if (name.at(0) == '"') { + insertNewlineAfter(pool, node, 0); + } + } + } +} + +std::string to_string(const goos::Object& obj, int line_length) { + NodePool pool; + std::vector tokens; + add_to_token_list(obj, &tokens); + assert(!tokens.empty()); + std::string pretty; + + // build linked list of nodes + PrettyPrinterNode* head = pool.allocate(&tokens[0]); + PrettyPrinterNode* node = head; + head->line = 0; + head->offset = 0; + head->lineIndent = 0; + int offset = head->tok->toString().length(); + for (size_t i = 1; i < tokens.size(); i++) { + node->next = pool.allocate(&tokens[i]); + node->next->prev = node; + node = node->next; + node->line = 0; + node->offset = offset; + offset += node->tok->toString().length(); + node->lineIndent = 0; + } + + // attach parens. + std::vector parenStack; + parenStack.push_back(nullptr); + for (PrettyPrinterNode* n = head; n; n = n->next) { + if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) { + parenStack.push_back(n); + } else if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) { + n->paren = parenStack.back(); + parenStack.back()->paren = n; + parenStack.pop_back(); + } else { + n->paren = parenStack.back(); + } + } + assert(parenStack.size() == 1); + assert(!parenStack.back()); + + insertSpecialBreaks(pool, head); + propagatePretty(pool, head, line_length); + insertBreaksAsNeeded(pool, head, line_length); + + // write to string + bool newline_prev = true; + for (PrettyPrinterNode* n = head; n; n = n->next) { + if (n->is_line_separator) { + pretty.push_back('\n'); + newline_prev = true; + } else { + if (newline_prev) { + pretty.append(n->lineIndent, ' '); + newline_prev = false; + if (n->tok->kind == FormToken::TokenKind::WHITESPACE) + continue; + } + pretty.append(n->tok->toString()); + } + } + + return pretty; +} + +goos::Reader pretty_printer_reader; + +goos::Reader& get_pretty_printer_reader() { + return pretty_printer_reader; +} + +goos::Object to_symbol(const std::string& str) { + return goos::SymbolObject::make_new(pretty_printer_reader.symbolTable, str); +} + +goos::Object build_list(const std::string& str) { + return build_list(to_symbol(str)); +} + +goos::Object build_list(const goos::Object& obj) { + return goos::PairObject::make_new(obj, goos::EmptyListObject::make_new()); +} + +goos::Object build_list(const std::vector& objects) { + if (objects.empty()) { + return goos::EmptyListObject::make_new(); + } else { + return build_list(objects.data(), objects.size()); + } +} + +// build a list out of an array of forms +goos::Object build_list(const goos::Object* objects, int count) { + assert(count); + auto car = objects[0]; + goos::Object cdr; + if (count - 1) { + cdr = build_list(objects + 1, count - 1); + } else { + cdr = goos::EmptyListObject::make_new(); + } + return goos::PairObject::make_new(car, cdr); +} + +// build a list out of a vector of strings that are converted to symbols +goos::Object build_list(const std::vector& symbols) { + if (symbols.empty()) { + return goos::EmptyListObject::make_new(); + } + std::vector f; + f.reserve(symbols.size()); + for (auto& x : symbols) { + f.push_back(to_symbol(x)); + } + return build_list(f.data(), f.size()); +} +} // namespace pretty_print \ No newline at end of file diff --git a/common/goos/PrettyPrinter.h b/common/goos/PrettyPrinter.h new file mode 100644 index 0000000000..c971d3748d --- /dev/null +++ b/common/goos/PrettyPrinter.h @@ -0,0 +1,49 @@ +/*! + * @file PrettyPrinter.h + * A Pretty Printer for GOOS. + */ + +#pragma once +#include +#include +#include "common/goos/Object.h" +#include "common/goos/Reader.h" + +namespace pretty_print { +// main pretty print function +std::string to_string(const goos::Object& obj, int line_length = 80); + +// string -> object (as a symbol) +goos::Object to_symbol(const std::string& str); + +// list with a single symbol from a string +goos::Object build_list(const std::string& str); + +// wrap an object in a list +goos::Object build_list(const goos::Object& obj); + +// build a list out of a vector of forms +goos::Object build_list(const std::vector& objects); + +// build a list out of an array of forms +goos::Object build_list(const goos::Object* objects, int count); + +// build a list out of a vector of strings that are converted to symbols +goos::Object build_list(const std::vector& symbols); + +// fancy wrapper functions. Due to template magic these can call each other +// and accept mixed arguments! + +template +goos::Object build_list(const std::string& str, Args... rest) { + return goos::PairObject::make_new(to_symbol(str), build_list(rest...)); +} + +template +goos::Object build_list(const goos::Object& car, Args... rest) { + return goos::PairObject::make_new(car, build_list(rest...)); +} + +goos::Reader& get_pretty_printer_reader(); + +} // namespace pretty_print diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index d1b37b8edc..d48df23840 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -1,5 +1,4 @@ add_executable(decompiler - util/LispPrint.cpp main.cpp ObjectFile/ObjectFileDB.cpp Disasm/Instruction.cpp @@ -11,7 +10,6 @@ add_executable(decompiler Function/Function.cpp util/FileIO.cpp config.cpp - util/LispPrint.cpp util/DecompilerTypeSystem.cpp Function/BasicBlocks.cpp Disasm/InstructionMatching.cpp @@ -20,6 +18,7 @@ add_executable(decompiler IR/IR.cpp) target_link_libraries(decompiler + goos minilzo common_util type_system diff --git a/decompiler/Function/CfgVtx.cpp b/decompiler/Function/CfgVtx.cpp index 31bdae7d9b..cc09a1c285 100644 --- a/decompiler/Function/CfgVtx.cpp +++ b/decompiler/Function/CfgVtx.cpp @@ -140,8 +140,8 @@ std::string BlockVtx::to_string() { } } -std::shared_ptr
BlockVtx::to_form() { - return toForm("b" + std::to_string(block_id)); +goos::Object BlockVtx::to_form() { + return pretty_print::to_symbol("b" + std::to_string(block_id)); } std::string SequenceVtx::to_string() { @@ -152,117 +152,117 @@ std::string SequenceVtx::to_string() { return result; } -std::shared_ptr SequenceVtx::to_form() { - std::vector> forms; - forms.push_back(toForm("seq")); +goos::Object SequenceVtx::to_form() { + std::vector forms; + forms.push_back(pretty_print::to_symbol("seq")); for (auto* x : seq) { forms.push_back(x->to_form()); } - return buildList(forms); + return pretty_print::build_list(forms); } std::string EntryVtx::to_string() { return "ENTRY"; } -std::shared_ptr EntryVtx::to_form() { - return toForm("entry"); +goos::Object EntryVtx::to_form() { + return pretty_print::to_symbol("entry"); } std::string ExitVtx::to_string() { return "EXIT"; } -std::shared_ptr ExitVtx::to_form() { - return toForm("exit"); +goos::Object ExitVtx::to_form() { + return pretty_print::to_symbol("exit"); } std::string CondWithElse::to_string() { return "CONDWE" + std::to_string(uid); } -std::shared_ptr CondWithElse::to_form() { - std::vector> forms; - forms.push_back(toForm("cond")); +goos::Object CondWithElse::to_form() { + std::vector forms; + forms.push_back(pretty_print::to_symbol("cond")); for (const auto& x : entries) { - std::vector> e = {x.condition->to_form(), x.body->to_form()}; - forms.push_back(buildList(e)); + std::vector e = {x.condition->to_form(), x.body->to_form()}; + forms.push_back(pretty_print::build_list(e)); } - std::vector> e = {toForm("else"), else_vtx->to_form()}; - forms.push_back(buildList(e)); - return buildList(forms); + std::vector e = {pretty_print::to_symbol("else"), else_vtx->to_form()}; + forms.push_back(pretty_print::build_list(e)); + return pretty_print::build_list(forms); } std::string CondNoElse::to_string() { return "CONDNE" + std::to_string(uid); } -std::shared_ptr CondNoElse::to_form() { - std::vector> forms; - forms.push_back(toForm("cond")); +goos::Object CondNoElse::to_form() { + std::vector forms; + forms.push_back(pretty_print::to_symbol("cond")); for (const auto& x : entries) { - std::vector> e = {x.condition->to_form(), x.body->to_form()}; - forms.push_back(buildList(e)); + std::vector e = {x.condition->to_form(), x.body->to_form()}; + forms.push_back(pretty_print::build_list(e)); } - return buildList(forms); + return pretty_print::build_list(forms); } std::string WhileLoop::to_string() { return "WHL" + std::to_string(uid); } -std::shared_ptr WhileLoop::to_form() { - std::vector> forms = {toForm("while"), condition->to_form(), - body->to_form()}; - return buildList(forms); +goos::Object WhileLoop::to_form() { + std::vector forms = {pretty_print::to_symbol("while"), condition->to_form(), + body->to_form()}; + return pretty_print::build_list(forms); } std::string UntilLoop::to_string() { return "UNTL" + std::to_string(uid); } -std::shared_ptr UntilLoop::to_form() { - std::vector> forms = {toForm("until"), condition->to_form(), - body->to_form()}; - return buildList(forms); +goos::Object UntilLoop::to_form() { + std::vector forms = {pretty_print::to_symbol("until"), condition->to_form(), + body->to_form()}; + return pretty_print::build_list(forms); } std::string UntilLoop_single::to_string() { return "UNTLS" + std::to_string(uid); } -std::shared_ptr UntilLoop_single::to_form() { - std::vector> forms = {toForm("until1"), block->to_form()}; - return buildList(forms); +goos::Object UntilLoop_single::to_form() { + std::vector forms = {pretty_print::to_symbol("until1"), block->to_form()}; + return pretty_print::build_list(forms); } std::string InfiniteLoopBlock::to_string() { return "INFL" + std::to_string(uid); } -std::shared_ptr InfiniteLoopBlock::to_form() { - std::vector> forms = {toForm("inf-loop"), block->to_form()}; - return buildList(forms); +goos::Object InfiniteLoopBlock::to_form() { + std::vector forms = {pretty_print::to_symbol("inf-loop"), block->to_form()}; + return pretty_print::build_list(forms); } std::string ShortCircuit::to_string() { return "SC" + std::to_string(uid); } -std::shared_ptr ShortCircuit::to_form() { - std::vector> forms; - forms.push_back(toForm("sc")); +goos::Object ShortCircuit::to_form() { + std::vector forms; + forms.push_back(pretty_print::to_symbol("sc")); for (const auto& x : entries) { forms.push_back(x->to_form()); } - return buildList(forms); + return pretty_print::build_list(forms); } /* -std::shared_ptr IfElseVtx::to_form() { - std::vector> forms = {toForm("if"), condition->to_form(), +goos::Object IfElseVtx::to_form() { + std::vector forms = {pretty_print::to_symbol("if"), condition->to_form(), true_case->to_form(), false_case->to_form()}; - return buildList(forms); + return pretty_print::build_list(forms); } std::string IfElseVtx::to_string() { @@ -274,10 +274,10 @@ std::string GotoEnd::to_string() { return "goto_end" + std::to_string(uid); } -std::shared_ptr GotoEnd::to_form() { - std::vector> forms = {toForm("return-from-function"), body->to_form(), - unreachable_block->to_form()}; - return buildList(forms); +goos::Object GotoEnd::to_form() { + std::vector forms = {pretty_print::to_symbol("return-from-function"), + body->to_form(), unreachable_block->to_form()}; + return pretty_print::build_list(forms); } ControlFlowGraph::ControlFlowGraph() { @@ -361,17 +361,17 @@ CfgVtx* ControlFlowGraph::get_single_top_level() { * Turn into a form. If fully resolved, prints the nested control flow. Otherwise puts all the * ungrouped stuff into an "(ungrouped ...)" form and prints that. */ -std::shared_ptr ControlFlowGraph::to_form() { +goos::Object ControlFlowGraph::to_form() { if (get_top_level_vertices_count() == 1) { return get_single_top_level()->to_form(); } else { - std::vector> forms = {toForm("ungrouped")}; + std::vector forms = {pretty_print::to_symbol("ungrouped")}; for (auto* x : m_node_pool) { if (!x->parent && x != entry() && x != exit()) { forms.push_back(x->to_form()); } } - return buildList(forms); + return pretty_print::build_list(forms); } } @@ -380,8 +380,7 @@ std::shared_ptr ControlFlowGraph::to_form() { * ungrouped stuff into an "(ungrouped ...)" form and prints that. */ std::string ControlFlowGraph::to_form_string() { - // todo - fix bug in pretty printing and reduce this to 80! - return to_form()->toStringPretty(0, 140); + return pretty_print::to_string(to_form()); } // bool ControlFlowGraph::compact_top_level() { diff --git a/decompiler/Function/CfgVtx.h b/decompiler/Function/CfgVtx.h index a19abb30cf..e3f46eb55f 100644 --- a/decompiler/Function/CfgVtx.h +++ b/decompiler/Function/CfgVtx.h @@ -6,7 +6,7 @@ #include #include #include -#include "decompiler/util/LispPrint.h" +#include "common/goos/PrettyPrinter.h" /*! * In v, find an item equal to old, and replace it with replace. @@ -61,8 +61,8 @@ void replace_exactly_one_in(std::vector& v, T old, T replace) { */ class CfgVtx { public: - virtual std::string to_string() = 0; // convert to a single line string for debugging - virtual std::shared_ptr to_form() = 0; // recursive print as LISP form. + virtual std::string to_string() = 0; // convert to a single line string for debugging + virtual goos::Object to_form() = 0; // recursive print as LISP form. virtual ~CfgVtx() = default; CfgVtx* parent = nullptr; // parent structure, or nullptr if top level @@ -125,7 +125,7 @@ class CfgVtx { class EntryVtx : public CfgVtx { public: EntryVtx() = default; - std::shared_ptr to_form() override; + goos::Object to_form() override; std::string to_string() override; }; @@ -135,7 +135,7 @@ class EntryVtx : public CfgVtx { class ExitVtx : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; }; /*! @@ -145,7 +145,7 @@ class BlockVtx : public CfgVtx { public: explicit BlockVtx(int id) : block_id(id) {} std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; int block_id = -1; // which block are we? bool is_early_exit_block = false; // are we an empty block at the end for early exits to jump to? }; @@ -157,7 +157,7 @@ class BlockVtx : public CfgVtx { class SequenceVtx : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; std::vector seq; }; @@ -169,7 +169,7 @@ class SequenceVtx : public CfgVtx { class CondWithElse : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; struct Entry { Entry() = default; @@ -190,7 +190,7 @@ class CondWithElse : public CfgVtx { class CondNoElse : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; struct Entry { Entry() = default; @@ -205,7 +205,7 @@ class CondNoElse : public CfgVtx { class WhileLoop : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; CfgVtx* condition = nullptr; CfgVtx* body = nullptr; @@ -214,7 +214,7 @@ class WhileLoop : public CfgVtx { class UntilLoop : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; CfgVtx* condition = nullptr; CfgVtx* body = nullptr; @@ -223,7 +223,7 @@ class UntilLoop : public CfgVtx { class UntilLoop_single : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; CfgVtx* block = nullptr; }; @@ -231,21 +231,21 @@ class UntilLoop_single : public CfgVtx { class ShortCircuit : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; std::vector entries; }; class InfiniteLoopBlock : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; CfgVtx* block; }; class GotoEnd : public CfgVtx { public: std::string to_string() override; - std::shared_ptr to_form() override; + goos::Object to_form() override; CfgVtx* body = nullptr; CfgVtx* unreachable_block = nullptr; }; @@ -260,7 +260,7 @@ class ControlFlowGraph { ControlFlowGraph(); ~ControlFlowGraph(); - std::shared_ptr to_form(); + goos::Object to_form(); std::string to_form_string(); std::string to_dot(); int get_top_level_vertices_count(); diff --git a/decompiler/IR/BasicOpBuilder.cpp b/decompiler/IR/BasicOpBuilder.cpp index aa677aa000..0c27140b8b 100644 --- a/decompiler/IR/BasicOpBuilder.cpp +++ b/decompiler/IR/BasicOpBuilder.cpp @@ -5,10 +5,6 @@ namespace { -struct ConvertState { - void reset() {} -}; - std::shared_ptr make_set(IR_Set::Kind kind, const std::shared_ptr& dst, const std::shared_ptr& src) { @@ -1122,6 +1118,7 @@ std::shared_ptr try_lwu(Instruction& i0, Instruction& i3, Instruction& i4, int idx) { + (void)idx; auto s6 = make_gpr(Reg::S6); if (i0.kind == InstructionKind::LWU && i0.get_dst(0).is_reg(s6) && i0.get_src(0).get_imm() == 44 && i0.get_src(1).is_reg(s6) && @@ -1138,8 +1135,6 @@ std::shared_ptr try_lwu(Instruction& i0, } // namespace void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file) { - ConvertState state; - for (int instr = block.start_word; instr < block.end_word; instr++) { auto& i = func->instructions.at(instr); @@ -1400,7 +1395,6 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec // everything failed if (!result) { - state.reset(); // temp hack for debug: printf("Instruction -> BasicOp failed on %s\n", i.to_string(*file).c_str()); func->add_basic_op(std::make_shared(), instr, instr + 1); diff --git a/decompiler/IR/IR.cpp b/decompiler/IR/IR.cpp index 2b6ba34187..b06b4ea117 100644 --- a/decompiler/IR/IR.cpp +++ b/decompiler/IR/IR.cpp @@ -2,19 +2,21 @@ #include "decompiler/ObjectFile/LinkedObjectFile.h" std::string IR::print(const LinkedObjectFile& file) const { - return to_form(file)->toStringPretty(); + // return to_form(file)->toStringPretty(); + return pretty_print::to_string(to_form(file)); } -std::shared_ptr IR_Register::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Register::to_form(const LinkedObjectFile& file) const { (void)file; - return toForm(reg.to_charp()); + return pretty_print::to_symbol(reg.to_charp()); } -std::shared_ptr IR_Set::to_form(const LinkedObjectFile& file) const { - return buildList(toForm("set!"), dst->to_form(file), src->to_form(file)); +goos::Object IR_Set::to_form(const LinkedObjectFile& file) const { + return pretty_print::build_list(pretty_print::to_symbol("set!"), dst->to_form(file), + src->to_form(file)); } -std::shared_ptr IR_Store::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Store::to_form(const LinkedObjectFile& file) const { std::string store_operator; switch (kind) { case FLOAT: @@ -45,30 +47,31 @@ std::shared_ptr IR_Store::to_form(const LinkedObjectFile& file) const { assert(false); } - return buildList(toForm(store_operator), dst->to_form(file), src->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(store_operator), dst->to_form(file), + src->to_form(file)); } -std::shared_ptr IR_Failed::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Failed::to_form(const LinkedObjectFile& file) const { (void)file; - return buildList("INVALID-OPERATION"); + return pretty_print::build_list("INVALID-OPERATION"); } -std::shared_ptr IR_Symbol::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Symbol::to_form(const LinkedObjectFile& file) const { (void)file; - return toForm("'" + name); + return pretty_print::to_symbol("'" + name); } -std::shared_ptr IR_SymbolValue::to_form(const LinkedObjectFile& file) const { +goos::Object IR_SymbolValue::to_form(const LinkedObjectFile& file) const { (void)file; - return toForm(name); + return pretty_print::to_symbol(name); } -std::shared_ptr IR_StaticAddress::to_form(const LinkedObjectFile& file) const { - // return buildList(toForm("&"), file.get_label_name(label_id)); - return toForm(file.get_label_name(label_id)); +goos::Object IR_StaticAddress::to_form(const LinkedObjectFile& file) const { + // return pretty_print::build_list(pretty_print::to_symbol("&"), file.get_label_name(label_id)); + return pretty_print::to_symbol(file.get_label_name(label_id)); } -std::shared_ptr IR_Load::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Load::to_form(const LinkedObjectFile& file) const { std::string load_operator; switch (kind) { case FLOAT: @@ -113,10 +116,10 @@ std::shared_ptr IR_Load::to_form(const LinkedObjectFile& file) const { default: assert(false); } - return buildList(toForm(load_operator), location->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(load_operator), location->to_form(file)); } -std::shared_ptr IR_FloatMath2::to_form(const LinkedObjectFile& file) const { +goos::Object IR_FloatMath2::to_form(const LinkedObjectFile& file) const { std::string math_operator; switch (kind) { case DIV: @@ -141,10 +144,11 @@ std::shared_ptr IR_FloatMath2::to_form(const LinkedObjectFile& file) const assert(false); } - return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file), + arg1->to_form(file)); } -std::shared_ptr IR_IntMath2::to_form(const LinkedObjectFile& file) const { +goos::Object IR_IntMath2::to_form(const LinkedObjectFile& file) const { std::string math_operator; switch (kind) { case ADD: @@ -195,10 +199,11 @@ std::shared_ptr IR_IntMath2::to_form(const LinkedObjectFile& file) const { default: assert(false); } - return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file), + arg1->to_form(file)); } -std::shared_ptr IR_IntMath1::to_form(const LinkedObjectFile& file) const { +goos::Object IR_IntMath1::to_form(const LinkedObjectFile& file) const { std::string math_operator; switch (kind) { case NOT: @@ -207,10 +212,10 @@ std::shared_ptr IR_IntMath1::to_form(const LinkedObjectFile& file) const { default: assert(false); } - return buildList(toForm(math_operator), arg->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file)); } -std::shared_ptr IR_FloatMath1::to_form(const LinkedObjectFile& file) const { +goos::Object IR_FloatMath1::to_form(const LinkedObjectFile& file) const { std::string math_operator; switch (kind) { case FLOAT_TO_INT: @@ -231,40 +236,43 @@ std::shared_ptr IR_FloatMath1::to_form(const LinkedObjectFile& file) const default: assert(false); } - return buildList(toForm(math_operator), arg->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file)); } -std::shared_ptr IR_Call::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Call::to_form(const LinkedObjectFile& file) const { (void)file; - return buildList("call!"); + return pretty_print::build_list("call!"); } -std::shared_ptr IR_IntegerConstant::to_form(const LinkedObjectFile& file) const { +goos::Object IR_IntegerConstant::to_form(const LinkedObjectFile& file) const { (void)file; - return toForm(std::to_string(value)); + return pretty_print::to_symbol(std::to_string(value)); } -std::shared_ptr BranchDelay::to_form(const LinkedObjectFile& file) const { +goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const { (void)file; switch (kind) { case NOP: - return buildList("nop"); + return pretty_print::build_list("nop"); case SET_REG_FALSE: - return buildList(toForm("set!"), destination->to_form(file), "'#f"); + return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file), + "'#f"); case SET_REG_TRUE: - return buildList(toForm("set!"), destination->to_form(file), "'#t"); + return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file), + "'#t"); case SET_REG_REG: - return buildList(toForm("set!"), destination->to_form(file), source->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file), + source->to_form(file)); case UNKNOWN: - return buildList("unknown-branch-delay"); + return pretty_print::build_list("unknown-branch-delay"); default: assert(false); } } -std::shared_ptr IR_Nop::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Nop::to_form(const LinkedObjectFile& file) const { (void)file; - return buildList("nop!"); + return pretty_print::build_list("nop!"); } int Condition::num_args() const { @@ -296,7 +304,7 @@ int Condition::num_args() const { } } -std::shared_ptr Condition::to_form(const LinkedObjectFile& file) const { +goos::Object Condition::to_form(const LinkedObjectFile& file) const { int nargs = num_args(); std::string condtion_operator; switch (kind) { @@ -362,30 +370,33 @@ std::shared_ptr Condition::to_form(const LinkedObjectFile& file) const { } if (nargs == 2) { - return buildList(toForm(condtion_operator), src0->to_form(file), src1->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(condtion_operator), src0->to_form(file), + src1->to_form(file)); } else if (nargs == 1) { if (condtion_operator.empty()) { return src0->to_form(file); } else { - return buildList(toForm(condtion_operator), src0->to_form(file)); + return pretty_print::build_list(pretty_print::to_symbol(condtion_operator), + src0->to_form(file)); } } else if (nargs == 0) { - return toForm(condtion_operator); + return pretty_print::to_symbol(condtion_operator); } else { assert(false); } } -std::shared_ptr IR_Branch::to_form(const LinkedObjectFile& file) const { - return buildList(toForm(likely ? "bl!" : "b!"), condition.to_form(file), - toForm(file.get_label_name(dest_label_idx)), branch_delay.to_form(file)); +goos::Object IR_Branch::to_form(const LinkedObjectFile& file) const { + return pretty_print::build_list( + pretty_print::to_symbol(likely ? "bl!" : "b!"), condition.to_form(file), + pretty_print::to_symbol(file.get_label_name(dest_label_idx)), branch_delay.to_form(file)); } -std::shared_ptr IR_Compare::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Compare::to_form(const LinkedObjectFile& file) const { return condition.to_form(file); } -std::shared_ptr IR_Suspend::to_form(const LinkedObjectFile& file) const { +goos::Object IR_Suspend::to_form(const LinkedObjectFile& file) const { (void)file; - return buildList("suspend!"); + return pretty_print::build_list("suspend!"); } \ No newline at end of file diff --git a/decompiler/IR/IR.h b/decompiler/IR/IR.h index e347afe9b5..cf79affee2 100644 --- a/decompiler/IR/IR.h +++ b/decompiler/IR/IR.h @@ -3,13 +3,13 @@ #include #include "decompiler/Disasm/Register.h" -#include "decompiler/util/LispPrint.h" +#include "common/goos/PrettyPrinter.h" class LinkedObjectFile; class IR { public: - virtual std::shared_ptr to_form(const LinkedObjectFile& file) const = 0; + virtual goos::Object to_form(const LinkedObjectFile& file) const = 0; std::string print(const LinkedObjectFile& file) const; bool is_basic_op = false; @@ -18,13 +18,13 @@ class IR { class IR_Failed : public IR { public: IR_Failed() = default; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Register : public IR { public: IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {} - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; Register reg; int instr_idx = -1; }; @@ -44,7 +44,7 @@ class IR_Set : public IR { } kind; IR_Set(Kind _kind, std::shared_ptr _dst, std::shared_ptr _src) : kind(_kind), dst(std::move(_dst)), src(std::move(_src)) {} - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; std::shared_ptr dst, src; std::shared_ptr clobber = nullptr; }; @@ -55,28 +55,28 @@ class IR_Store : public IR_Set { IR_Store(Kind _kind, std::shared_ptr _dst, std::shared_ptr _src, int _size) : IR_Set(IR_Set::LOAD, std::move(_dst), std::move(_src)), kind(_kind), size(_size) {} int size; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Symbol : public IR { public: IR_Symbol(std::string _name) : name(std::move(_name)) {} std::string name; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_SymbolValue : public IR { public: IR_SymbolValue(std::string _name) : name(std::move(_name)) {} std::string name; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_StaticAddress : public IR { public: IR_StaticAddress(int _label_id) : label_id(_label_id) {} int label_id = -1; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Load : public IR { @@ -87,7 +87,7 @@ class IR_Load : public IR { : kind(_kind), size(_size), location(_location) {} int size; std::shared_ptr location; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_FloatMath2 : public IR { @@ -96,7 +96,7 @@ class IR_FloatMath2 : public IR { IR_FloatMath2(Kind _kind, std::shared_ptr _arg0, std::shared_ptr _arg1) : kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {} std::shared_ptr arg0, arg1; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_FloatMath1 : public IR { @@ -104,7 +104,7 @@ class IR_FloatMath1 : public IR { enum Kind { FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT } kind; IR_FloatMath1(Kind _kind, std::shared_ptr _arg) : kind(_kind), arg(std::move(_arg)) {} std::shared_ptr arg; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_IntMath2 : public IR { @@ -129,7 +129,7 @@ class IR_IntMath2 : public IR { IR_IntMath2(Kind _kind, std::shared_ptr _arg0, std::shared_ptr _arg1) : kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {} std::shared_ptr arg0, arg1; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_IntMath1 : public IR { @@ -137,27 +137,27 @@ class IR_IntMath1 : public IR { enum Kind { NOT } kind; IR_IntMath1(Kind _kind, std::shared_ptr _arg) : kind(_kind), arg(std::move(_arg)) {} std::shared_ptr arg; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Call : public IR { public: IR_Call() = default; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_IntegerConstant : public IR { public: int64_t value; explicit IR_IntegerConstant(int64_t _value) : value(_value) {} - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; struct BranchDelay { enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, UNKNOWN } kind; std::shared_ptr destination = nullptr, source = nullptr; BranchDelay(Kind _kind) : kind(_kind) {} - std::shared_ptr to_form(const LinkedObjectFile& file) const; + goos::Object to_form(const LinkedObjectFile& file) const; }; struct Condition { @@ -199,7 +199,7 @@ struct Condition { } int num_args() const; - std::shared_ptr to_form(const LinkedObjectFile& file) const; + goos::Object to_form(const LinkedObjectFile& file) const; std::shared_ptr src0, src1, clobber; }; @@ -216,7 +216,7 @@ class IR_Branch : public IR { BranchDelay branch_delay; bool likely; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Compare : public IR { @@ -225,19 +225,19 @@ class IR_Compare : public IR { Condition condition; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Nop : public IR { public: IR_Nop() = default; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; class IR_Suspend : public IR { public: IR_Suspend() = default; - std::shared_ptr to_form(const LinkedObjectFile& file) const override; + goos::Object to_form(const LinkedObjectFile& file) const override; }; #endif // JAK_IR_H diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index 6f4249bd7f..5929ccb5db 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -714,7 +714,9 @@ std::string LinkedObjectFile::print_scripts() { if (label_id != -1) { auto& label = labels.at(label_id); if ((label.offset & 7) == 2) { - result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) + "\n"; + // result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) + + // "\n"; + result += pretty_print::to_string(to_form_script(seg, word_idx, already_printed)) + "\n"; } } } @@ -736,16 +738,14 @@ bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) { * Note : this takes the address of the car of the pair. which is perhaps a bit confusing * (in GOAL, this would be (&-> obj car)) */ -std::shared_ptr LinkedObjectFile::to_form_script(int seg, - int word_idx, - std::vector& seen) { +goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector& seen) { // the object to currently print. to start off, create pair from the car address we've been given. int goal_print_obj = word_idx * 4 + 2; // resulting form. we can't have a totally empty list (as an empty list looks like a symbol, // so it wouldn't be flagged), so it's safe to make this a pair. - auto result = std::make_shared(); - result->kind = FormKind::PAIR; + auto result = goos::PairObject::make_new(goos::EmptyListObject::make_new(), + goos::EmptyListObject::make_new()); // the current pair to fill out. auto fill = result; @@ -755,14 +755,14 @@ std::shared_ptr LinkedObjectFile::to_form_script(int seg, // check the thing to print is a a pair. if ((goal_print_obj & 7) == 2) { // first convert the car (again, with (&-> obj car)) - fill->pair[0] = to_form_script_object(seg, goal_print_obj - 2, seen); + fill.as_pair()->car = to_form_script_object(seg, goal_print_obj - 2, seen); seen.at(goal_print_obj / 4) = true; auto cdr_addr = goal_print_obj + 2; if (is_empty_list(seg, cdr_addr)) { // the list has ended! - fill->pair[1] = gSymbolTable.getEmptyPair(); + fill.as_pair()->cdr = goos::EmptyListObject::make_new(); return result; } else { // cdr object should be aligned. @@ -772,12 +772,12 @@ std::shared_ptr LinkedObjectFile::to_form_script(int seg, if (cdr_word.kind == LinkedWord::PTR && (labels.at(cdr_word.label_id).offset & 7) == 2) { // yes, proper list. add another pair and link it in to the list. goal_print_obj = labels.at(cdr_word.label_id).offset; - fill->pair[1] = std::make_shared(); - fill->pair[1]->kind = FormKind::PAIR; - fill = fill->pair[1]; + fill.as_pair()->cdr = goos::PairObject::make_new(goos::EmptyListObject::make_new(), + goos::EmptyListObject::make_new()); + fill = fill.as_pair()->cdr; } else { // improper list, put the last thing in and end - fill->pair[1] = to_form_script_object(seg, cdr_addr, seen); + fill.as_pair()->cdr = to_form_script_object(seg, cdr_addr, seen); return result; } } @@ -809,10 +809,10 @@ bool LinkedObjectFile::is_string(int seg, int byte_idx) { /*! * Convert a (pointer object) to some nice representation. */ -std::shared_ptr LinkedObjectFile::to_form_script_object(int seg, - int byte_idx, - std::vector& seen) { - std::shared_ptr result; +goos::Object LinkedObjectFile::to_form_script_object(int seg, + int byte_idx, + std::vector& seen) { + goos::Object result; switch (byte_idx & 7) { case 0: @@ -820,10 +820,10 @@ std::shared_ptr LinkedObjectFile::to_form_script_object(int seg, auto& word = words_by_seg.at(seg).at(byte_idx / 4); if (word.kind == LinkedWord::SYM_PTR) { // .symbol xxxx - result = toForm(word.symbol_name); + result = pretty_print::to_symbol(word.symbol_name); } else if (word.kind == LinkedWord::PLAIN_DATA) { // .word xxxxx - result = toForm(std::to_string(word.data)); + result = pretty_print::to_symbol(std::to_string(word.data)); } else if (word.kind == LinkedWord::PTR) { // might be a sub-list, or some other random pointer auto offset = labels.at(word.label_id).offset; @@ -832,14 +832,14 @@ std::shared_ptr LinkedObjectFile::to_form_script_object(int seg, result = to_form_script(seg, offset / 4, seen); } else { if (is_string(seg, offset)) { - result = toForm(get_goal_string(seg, offset / 4 - 1)); + result = pretty_print::to_symbol(get_goal_string(seg, offset / 4 - 1)); } else { // some random pointer, just print the label. - result = toForm(labels.at(word.label_id).name); + result = pretty_print::to_symbol(labels.at(word.label_id).name); } } } else if (word.kind == LinkedWord::EMPTY_PTR) { - result = gSymbolTable.getEmptyPair(); + result = goos::EmptyListObject::make_new(); } else { std::string debug; append_word_to_string(debug, word); diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index 7b535efbf9..7c18c6c4e5 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -15,7 +15,7 @@ #include #include "LinkedWord.h" #include "decompiler/Function/Function.h" -#include "decompiler/util/LispPrint.h" +#include "common/goos/PrettyPrinter.h" /*! * A label to a location in this object file. @@ -124,8 +124,8 @@ class LinkedObjectFile { std::vector