mirror of
https://github.com/open-goal/jak-project
synced 2026-07-07 06:05:15 -04:00
[Decompiler] Begin expression conversion, rearrange tests (#209)
* refactor tests and analysis passes * identity test working * combine test categories with only a few cases * more fixes
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
add_library(
|
||||
decomp
|
||||
SHARED
|
||||
|
||||
analysis/atomic_op_builder.cpp
|
||||
analysis/cfg_builder.cpp
|
||||
analysis/expression_build.cpp
|
||||
analysis/reg_usage.cpp
|
||||
analysis/variable_naming.cpp
|
||||
|
||||
data/game_count.cpp
|
||||
data/game_text.cpp
|
||||
data/StrFileReader.cpp
|
||||
@@ -22,17 +29,13 @@ add_library(
|
||||
IR/BasicOpBuilder.cpp
|
||||
IR/IR.cpp
|
||||
|
||||
IR2/atomic_op_builder.cpp
|
||||
IR2/AtomicOp.cpp
|
||||
IR2/AtomicOpForm.cpp
|
||||
IR2/AtomicOpTypeAnalysis.cpp
|
||||
IR2/cfg_builder.cpp
|
||||
IR2/Env.cpp
|
||||
IR2/expression_build.cpp
|
||||
IR2/Form.cpp
|
||||
IR2/FormExpressionAnalysis.cpp
|
||||
IR2/FormStack.cpp
|
||||
IR2/reg_usage.cpp
|
||||
IR2/variable_naming.cpp
|
||||
|
||||
ObjectFile/LinkedObjectFile.cpp
|
||||
ObjectFile/LinkedObjectFileCreation.cpp
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/util/TP_Type.h"
|
||||
// for RegSet:
|
||||
#include "decompiler/IR2/reg_usage.h"
|
||||
#include "decompiler/analysis/reg_usage.h"
|
||||
|
||||
namespace decompiler {
|
||||
class LinkedObjectFile;
|
||||
@@ -29,15 +29,6 @@ struct BasicBlock {
|
||||
int succ_ft = -1;
|
||||
int succ_branch = -1;
|
||||
|
||||
std::vector<RegSet> live, dead;
|
||||
RegSet use, defs;
|
||||
RegSet input, output;
|
||||
|
||||
bool op_has_reg_live_out(int basic_op_idx, Register reg) {
|
||||
auto& lv = live.at(basic_op_idx - start_basic_op);
|
||||
return lv.find(reg) != lv.end();
|
||||
}
|
||||
|
||||
BasicBlock(int _start_word, int _end_word) : start_word(_start_word), end_word(_end_word) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include "decompiler/IR2/atomic_op_builder.h"
|
||||
#include "decompiler/analysis/atomic_op_builder.h"
|
||||
#include "decompiler/Disasm/Instruction.h"
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "BasicBlocks.h"
|
||||
|
||||
@@ -104,7 +104,7 @@ bool Function::run_type_analysis_ir2(const TypeSpec& my_type,
|
||||
} catch (std::runtime_error& e) {
|
||||
fmt::print("Type prop fail on {}: {}\n", guessed_name.to_string(), e.what());
|
||||
warnings += ";; Type prop attempted and failed.\n";
|
||||
ir2.env.set_types(block_init_types, op_types);
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ bool Function::run_type_analysis_ir2(const TypeSpec& my_type,
|
||||
my_type.last_arg().print());
|
||||
}
|
||||
|
||||
ir2.env.set_types(block_init_types, op_types);
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ ConditionElement* IR2_Condition::get_as_form(FormPool& pool) const {
|
||||
}
|
||||
|
||||
FormElement* SetVarOp::get_as_form(FormPool& pool) const {
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_src);
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_src, m_my_idx);
|
||||
return pool.alloc_element<SetVarElement>(m_dst, source, is_sequence_point());
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ FormElement* StoreOp::get_as_form(FormPool& pool) const {
|
||||
}
|
||||
|
||||
FormElement* LoadVarOp::get_as_form(FormPool& pool) const {
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_src);
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_src, m_my_idx);
|
||||
auto load = pool.alloc_single_element_form<LoadSourceElement>(nullptr, source, m_size, m_kind);
|
||||
return pool.alloc_element<SetVarElement>(m_dst, load, true);
|
||||
}
|
||||
|
||||
+21
-1
@@ -3,6 +3,7 @@
|
||||
#include <algorithm>
|
||||
#include "Env.h"
|
||||
#include "Form.h"
|
||||
#include "decompiler/analysis/atomic_op_builder.h"
|
||||
|
||||
namespace decompiler {
|
||||
std::string Env::get_variable_name(Register reg, int atomic_idx, VariableMode mode) const {
|
||||
@@ -13,9 +14,28 @@ std::string Env::get_variable_name(Register reg, int atomic_idx, VariableMode mo
|
||||
* Update the Env with the result of the type analysis pass.
|
||||
*/
|
||||
void Env::set_types(const std::vector<TypeState>& block_init_types,
|
||||
const std::vector<TypeState>& op_end_types) {
|
||||
const std::vector<TypeState>& op_end_types,
|
||||
const FunctionAtomicOps& atomic_ops) {
|
||||
m_block_init_types = block_init_types;
|
||||
m_op_end_types = op_end_types;
|
||||
|
||||
// cache the init types (this ends up being faster)
|
||||
m_op_init_types.resize(op_end_types.size(), nullptr);
|
||||
for (int block_idx = 0; block_idx < int(m_block_init_types.size()); block_idx++) {
|
||||
int first_op = atomic_ops.block_id_to_first_atomic_op.at(block_idx);
|
||||
int end_op = atomic_ops.block_id_to_end_atomic_op.at(block_idx);
|
||||
if (end_op > first_op) {
|
||||
m_op_init_types.at(first_op) = &m_block_init_types.at(block_idx);
|
||||
for (int op_idx = first_op; op_idx < (end_op - 1); op_idx++) {
|
||||
m_op_init_types.at(op_idx + 1) = &m_op_end_types.at(op_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto x : m_op_init_types) {
|
||||
assert(x);
|
||||
}
|
||||
|
||||
m_has_types = true;
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -6,11 +6,12 @@
|
||||
#include "decompiler/util/TP_Type.h"
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "decompiler/IR2/IR2_common.h"
|
||||
#include "decompiler/IR2/reg_usage.h"
|
||||
#include "decompiler/analysis/reg_usage.h"
|
||||
|
||||
namespace decompiler {
|
||||
class LinkedObjectFile;
|
||||
class Form;
|
||||
struct FunctionAtomicOps;
|
||||
|
||||
struct VariableNames {
|
||||
struct VarInfo {
|
||||
@@ -68,6 +69,11 @@ class Env {
|
||||
return m_op_end_types.at(atomic_op_id);
|
||||
}
|
||||
|
||||
const TypeState& get_types_before_op(int atomic_op_id) const {
|
||||
assert(m_has_types);
|
||||
return *m_op_init_types.at(atomic_op_id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the types in registers at the beginning of this basic block, before any operations
|
||||
* have occurred.
|
||||
@@ -78,7 +84,8 @@ class Env {
|
||||
}
|
||||
|
||||
void set_types(const std::vector<TypeState>& block_init_types,
|
||||
const std::vector<TypeState>& op_end_types);
|
||||
const std::vector<TypeState>& op_end_types,
|
||||
const FunctionAtomicOps& atomic_ops);
|
||||
|
||||
void set_local_vars(const VariableNames& names) {
|
||||
m_var_names = names;
|
||||
@@ -102,5 +109,6 @@ class Env {
|
||||
bool m_has_types = false;
|
||||
std::vector<TypeState> m_block_init_types;
|
||||
std::vector<TypeState> m_op_end_types;
|
||||
std::vector<TypeState*> m_op_init_types;
|
||||
};
|
||||
} // namespace decompiler
|
||||
+124
-2
@@ -28,7 +28,7 @@ std::string FormElement::to_string(const Env& env) const {
|
||||
return to_form(env).print();
|
||||
}
|
||||
|
||||
void FormElement::push_to_stack(const Env& env, FormStack&) {
|
||||
void FormElement::push_to_stack(const Env& env, FormPool&, FormStack&) {
|
||||
throw std::runtime_error("push_to_stack not implemented for " + to_string(env));
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@ void Form::collect_vars(VariableSet& vars) const {
|
||||
// SimpleExpressionElement
|
||||
/////////////////////////////
|
||||
|
||||
SimpleExpressionElement::SimpleExpressionElement(SimpleExpression expr) : m_expr(std::move(expr)) {}
|
||||
SimpleExpressionElement::SimpleExpressionElement(SimpleExpression expr, int my_idx)
|
||||
: m_expr(std::move(expr)), m_my_idx(my_idx) {}
|
||||
|
||||
goos::Object SimpleExpressionElement::to_form(const Env& env) const {
|
||||
return m_expr.to_form(env.file->labels, &env);
|
||||
@@ -761,4 +762,125 @@ void ConditionalMoveFalseElement::collect_vars(VariableSet& vars) const {
|
||||
vars.insert(dest);
|
||||
source->collect_vars(vars);
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// GenericElement
|
||||
/////////////////////////////
|
||||
|
||||
GenericOperator GenericOperator::make_fixed(FixedOperatorKind kind) {
|
||||
GenericOperator op;
|
||||
op.m_kind = Kind::FIXED_OPERATOR;
|
||||
op.m_fixed_kind = kind;
|
||||
return op;
|
||||
}
|
||||
|
||||
void GenericOperator::collect_vars(VariableSet&) const {
|
||||
switch (m_kind) {
|
||||
case Kind::FIXED_OPERATOR:
|
||||
return;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object GenericOperator::to_form(const Env&) const {
|
||||
switch (m_kind) {
|
||||
case Kind::FIXED_OPERATOR:
|
||||
return pretty_print::to_symbol(fixed_operator_to_string(m_fixed_kind));
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericOperator::apply(const std::function<void(FormElement*)>&) {
|
||||
switch (m_kind) {
|
||||
case Kind::FIXED_OPERATOR:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericOperator::apply_form(const std::function<void(Form*)>&) {
|
||||
switch (m_kind) {
|
||||
case Kind::FIXED_OPERATOR:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
switch (kind) {
|
||||
case FixedOperatorKind::GPR_TO_FPR:
|
||||
return "gpr->fpr";
|
||||
case FixedOperatorKind::DIVISION:
|
||||
return "/";
|
||||
case FixedOperatorKind::ADDITION:
|
||||
return "+";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
GenericElement::GenericElement(GenericOperator op) : m_head(op) {}
|
||||
GenericElement::GenericElement(GenericOperator op, Form* arg) : m_head(op), m_elts({arg}) {}
|
||||
GenericElement::GenericElement(GenericOperator op, Form* arg0, Form* arg1)
|
||||
: m_head(op), m_elts({arg0, arg1}) {}
|
||||
GenericElement::GenericElement(GenericOperator op, std::vector<Form*> forms)
|
||||
: m_head(op), m_elts(std::move(forms)) {}
|
||||
|
||||
goos::Object GenericElement::to_form(const Env& env) const {
|
||||
std::vector<goos::Object> result;
|
||||
result.push_back(m_head.to_form(env));
|
||||
for (auto x : m_elts) {
|
||||
result.push_back(x->to_form(env));
|
||||
}
|
||||
return pretty_print::build_list(result);
|
||||
}
|
||||
|
||||
void GenericElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
m_head.apply(f);
|
||||
for (auto x : m_elts) {
|
||||
x->apply(f);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
m_head.apply_form(f);
|
||||
for (auto x : m_elts) {
|
||||
x->apply_form(f);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericElement::collect_vars(VariableSet& vars) const {
|
||||
m_head.collect_vars(vars);
|
||||
for (auto x : m_elts) {
|
||||
x->collect_vars(vars);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// CastElement
|
||||
/////////////////////////////
|
||||
|
||||
CastElement::CastElement(TypeSpec type, Form* source) : m_type(std::move(type)), m_source(source) {}
|
||||
|
||||
goos::Object CastElement::to_form(const Env& env) const {
|
||||
return pretty_print::build_list("the-as", m_type.print(), m_source->to_form(env));
|
||||
}
|
||||
|
||||
void CastElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
m_source->apply(f);
|
||||
}
|
||||
|
||||
void CastElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
m_source->apply_form(f);
|
||||
}
|
||||
|
||||
void CastElement::collect_vars(VariableSet& vars) const {
|
||||
m_source->collect_vars(vars);
|
||||
}
|
||||
} // namespace decompiler
|
||||
|
||||
+91
-32
@@ -30,7 +30,11 @@ class FormElement {
|
||||
std::string to_string(const Env& env) const;
|
||||
|
||||
// push the result of this operation to the operation stack
|
||||
virtual void push_to_stack(const Env& env, FormStack& stack);
|
||||
virtual void push_to_stack(const Env& env, FormPool& pool, FormStack& stack);
|
||||
virtual void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
|
||||
protected:
|
||||
friend class Form;
|
||||
@@ -42,18 +46,45 @@ class FormElement {
|
||||
*/
|
||||
class SimpleExpressionElement : public FormElement {
|
||||
public:
|
||||
explicit SimpleExpressionElement(SimpleExpression expr);
|
||||
explicit SimpleExpressionElement(SimpleExpression expr, int my_idx);
|
||||
|
||||
goos::Object to_form(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
bool is_sequence_point() const override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
// void push_to_stack(const Env& env, FormStack& stack) override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) override;
|
||||
|
||||
void update_from_stack_identity(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
void update_from_stack_gpr_to_fpr(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
void update_from_stack_fpr_to_gpr(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
void update_from_stack_div_s(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
void update_from_stack_add_i(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
|
||||
const SimpleExpression& expr() const { return m_expr; }
|
||||
|
||||
private:
|
||||
SimpleExpression m_expr;
|
||||
int m_my_idx;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -90,6 +121,10 @@ class LoadSourceElement : public FormElement {
|
||||
int size() const { return m_size; }
|
||||
LoadVarOp::Kind kind() const { return m_kind; }
|
||||
const Form* location() const { return m_addr; }
|
||||
virtual void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result);
|
||||
|
||||
private:
|
||||
Form* m_addr = nullptr;
|
||||
@@ -108,6 +143,7 @@ class SimpleAtomElement : public FormElement {
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
// void push_to_stack(const Env& env, FormStack& stack) override;
|
||||
|
||||
private:
|
||||
SimpleAtom m_atom;
|
||||
@@ -124,6 +160,7 @@ class SetVarElement : public FormElement {
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
bool is_sequence_point() const override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
|
||||
|
||||
const Variable& dst() const { return m_dst; }
|
||||
const Form* src() const { return m_src; }
|
||||
@@ -464,36 +501,56 @@ class ConditionalMoveFalseElement : public FormElement {
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
};
|
||||
|
||||
///*!
|
||||
// * A GenericOperator is the head of a GenericElement.
|
||||
// * It is used for the final output.
|
||||
// */
|
||||
// class GenericOperator {
|
||||
// public:
|
||||
// enum class Kind {
|
||||
// FIXED_FUNCTION_CALL,
|
||||
// VAR_FUNCTION_CALL,
|
||||
// FIXED_OPERATOR
|
||||
// };
|
||||
//
|
||||
// private:
|
||||
// // if we're a VAR_FUNCTION_CALL, this should contain the expression to get the function
|
||||
// Form* m_function_val;
|
||||
//
|
||||
// //std::string
|
||||
//
|
||||
//};
|
||||
//
|
||||
// class GenericElement : public FormElement {
|
||||
// public:
|
||||
// goos::Object to_form(const Env& env) const override;
|
||||
// void apply(const std::function<void(FormElement*)>& f) override;
|
||||
// void apply_form(const std::function<void(Form*)>& f) override;
|
||||
// void collect_vars(VariableSet& vars) const override;
|
||||
// private:
|
||||
// GenericOperator m_head;
|
||||
// std::vector<Form*> m_elts;
|
||||
//};
|
||||
std::string fixed_operator_to_string(FixedOperatorKind kind);
|
||||
|
||||
/*!
|
||||
* A GenericOperator is the head of a GenericElement.
|
||||
* It is used for the final output.
|
||||
*/
|
||||
class GenericOperator {
|
||||
public:
|
||||
enum class Kind { FIXED_OPERATOR, INVALID };
|
||||
|
||||
static GenericOperator make_fixed(FixedOperatorKind kind);
|
||||
void collect_vars(VariableSet& vars) const;
|
||||
goos::Object to_form(const Env& env) const;
|
||||
void apply(const std::function<void(FormElement*)>& f);
|
||||
void apply_form(const std::function<void(Form*)>& f);
|
||||
|
||||
private:
|
||||
Kind m_kind = Kind::INVALID;
|
||||
FixedOperatorKind m_fixed_kind = FixedOperatorKind::INVALID;
|
||||
};
|
||||
|
||||
class GenericElement : public FormElement {
|
||||
public:
|
||||
explicit GenericElement(GenericOperator op);
|
||||
GenericElement(GenericOperator op, Form* arg);
|
||||
GenericElement(GenericOperator op, Form* arg0, Form* arg1);
|
||||
GenericElement(GenericOperator op, std::vector<Form*> forms);
|
||||
|
||||
goos::Object to_form(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
|
||||
private:
|
||||
GenericOperator m_head;
|
||||
std::vector<Form*> m_elts;
|
||||
};
|
||||
|
||||
class CastElement : public FormElement {
|
||||
public:
|
||||
explicit CastElement(TypeSpec type, Form* source);
|
||||
goos::Object to_form(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
|
||||
private:
|
||||
TypeSpec m_type;
|
||||
Form* m_source = nullptr;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A Form is a wrapper around one or more FormElements.
|
||||
@@ -559,6 +616,8 @@ class Form {
|
||||
void apply_form(const std::function<void(Form*)>& f);
|
||||
void collect_vars(VariableSet& vars) const;
|
||||
|
||||
void update_children_from_stack(const Env& env, FormPool& pool, FormStack& stack);
|
||||
|
||||
FormElement* parent_element = nullptr;
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "Form.h"
|
||||
#include "FormStack.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
namespace {
|
||||
|
||||
void update_var_from_stack_helper(int my_idx,
|
||||
Variable input,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
auto& ri = env.reg_use().op.at(my_idx);
|
||||
if (ri.consumes.find(input.reg()) != ri.consumes.end()) {
|
||||
// is consumed.
|
||||
auto stack_val = stack.pop_reg(input);
|
||||
if (stack_val) {
|
||||
for (auto x : stack_val->elts()) {
|
||||
result->push_back(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto elt =
|
||||
pool.alloc_element<SimpleExpressionElement>(SimpleAtom::make_var(input).as_expr(), my_idx);
|
||||
result->push_back(elt);
|
||||
}
|
||||
|
||||
Form* update_var_from_stack_to_form(int my_idx,
|
||||
Variable input,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack) {
|
||||
std::vector<FormElement*> elts;
|
||||
update_var_from_stack_helper(my_idx, input, env, pool, stack, &elts);
|
||||
return pool.alloc_sequence_form(nullptr, elts);
|
||||
}
|
||||
|
||||
bool is_float_type(const Env& env, int my_idx, Variable var) {
|
||||
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec();
|
||||
return type == TypeSpec("float");
|
||||
}
|
||||
|
||||
bool is_int_type(const Env& env, int my_idx, Variable var) {
|
||||
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec();
|
||||
return type == TypeSpec("int");
|
||||
}
|
||||
|
||||
bool is_uint_type(const Env& env, int my_idx, Variable var) {
|
||||
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec();
|
||||
return type == TypeSpec("uint");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Form::update_children_from_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
std::vector<FormElement*> new_elts;
|
||||
for (auto& elt : m_elements) {
|
||||
elt->update_from_stack(env, pool, stack, &new_elts);
|
||||
}
|
||||
m_elements = new_elts;
|
||||
}
|
||||
|
||||
void FormElement::update_from_stack(const Env& env,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>*) {
|
||||
throw std::runtime_error(fmt::format("update_from_stack NYI for {}", to_string(env)));
|
||||
}
|
||||
|
||||
void LoadSourceElement::update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
m_addr->update_children_from_stack(env, pool, stack);
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_identity(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
auto& arg = m_expr.get_arg(0);
|
||||
if (arg.is_var()) {
|
||||
update_var_from_stack_helper(m_my_idx, arg.var(), env, pool, stack, result);
|
||||
} else if (arg.is_static_addr()) {
|
||||
// for now, do nothing.
|
||||
result->push_back(this);
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
fmt::format("SimpleExpressionElement::update_from_stack NYI for {}", to_string(env)));
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_gpr_to_fpr(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
auto src = m_expr.get_arg(0);
|
||||
auto src_type = env.get_types_before_op(m_my_idx).get(src.var().reg());
|
||||
if (src_type.typespec() == TypeSpec("float")) {
|
||||
// set ourself to identity.
|
||||
m_expr = src.as_expr();
|
||||
// then go again.
|
||||
update_from_stack(env, pool, stack, result);
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("GPR -> FPR applied to a {}", src_type.print()));
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_fpr_to_gpr(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
auto src = m_expr.get_arg(0);
|
||||
auto src_type = env.get_types_before_op(m_my_idx).get(src.var().reg());
|
||||
if (src_type.typespec() == TypeSpec("float")) {
|
||||
// set ourself to identity.
|
||||
m_expr = src.as_expr();
|
||||
// then go again.
|
||||
update_from_stack(env, pool, stack, result);
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("FPR -> GPR applied to a {}", src_type.print()));
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_div_s(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
if (is_float_type(env, m_my_idx, m_expr.get_arg(0).var()) &&
|
||||
is_float_type(env, m_my_idx, m_expr.get_arg(1).var())) {
|
||||
// todo - check the order here
|
||||
auto arg0 = update_var_from_stack_to_form(m_my_idx, m_expr.get_arg(0).var(), env, pool, stack);
|
||||
auto arg1 = update_var_from_stack_to_form(m_my_idx, m_expr.get_arg(1).var(), env, pool, stack);
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::DIVISION), arg0, arg1);
|
||||
result->push_back(new_form);
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Floating point division attempted on invalid types."));
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
auto arg1_i = is_int_type(env, m_my_idx, m_expr.get_arg(1).var());
|
||||
auto arg1_u = is_uint_type(env, m_my_idx, m_expr.get_arg(1).var());
|
||||
|
||||
auto arg0 = update_var_from_stack_to_form(m_my_idx, m_expr.get_arg(0).var(), env, pool, stack);
|
||||
auto arg1 = update_var_from_stack_to_form(m_my_idx, m_expr.get_arg(1).var(), env, pool, stack);
|
||||
|
||||
if ((arg0_i && arg1_i) || (arg0_u && arg1_u)) {
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::ADDITION), arg0, arg1);
|
||||
result->push_back(new_form);
|
||||
} else {
|
||||
auto cast = pool.alloc_single_element_form<CastElement>(
|
||||
nullptr, TypeSpec(arg0_i ? "int" : "uint"), arg1);
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::ADDITION), arg0, cast);
|
||||
result->push_back(new_form);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result) {
|
||||
switch (m_expr.kind()) {
|
||||
case SimpleExpression::Kind::IDENTITY:
|
||||
update_from_stack_identity(env, pool, stack, result);
|
||||
break;
|
||||
case SimpleExpression::Kind::GPR_TO_FPR:
|
||||
update_from_stack_gpr_to_fpr(env, pool, stack, result);
|
||||
break;
|
||||
case SimpleExpression::Kind::FPR_TO_GPR:
|
||||
update_from_stack_fpr_to_gpr(env, pool, stack, result);
|
||||
break;
|
||||
case SimpleExpression::Kind::DIV_S:
|
||||
update_from_stack_div_s(env, pool, stack, result);
|
||||
break;
|
||||
case SimpleExpression::Kind::ADD:
|
||||
update_from_stack_add_i(env, pool, stack, result);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
fmt::format("SimpleExpressionElement::update_from_stack NYI for {}", to_string(env)));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// SetVarElement
|
||||
///////////////////
|
||||
|
||||
void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
m_src->update_children_from_stack(env, pool, stack);
|
||||
if (m_src->is_single_element()) {
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
src_as_se->expr().get_arg(0).is_var()) {
|
||||
stack.push_value_to_reg(m_dst, m_src, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_value_to_reg(m_dst, m_src, true);
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -53,7 +53,7 @@ Form* FormStack::pop_reg(const Variable& var) {
|
||||
for (size_t i = m_stack.size(); i-- > 0;) {
|
||||
auto& entry = m_stack.at(i);
|
||||
if (entry.active) {
|
||||
if (entry.destination == var) {
|
||||
if (entry.destination->reg() == var.reg()) {
|
||||
entry.active = false;
|
||||
assert(entry.source);
|
||||
return entry.source;
|
||||
@@ -88,4 +88,24 @@ std::vector<FormElement*> FormStack::rewrite(FormPool& pool) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<FormElement*> FormStack::rewrite_to_get_reg(FormPool& pool, Register reg) {
|
||||
// first, rewrite as normal.
|
||||
auto default_result = rewrite(pool);
|
||||
|
||||
// try a few different ways to "naturally" rewrite this so the value of the form is the
|
||||
// value in the given register.
|
||||
|
||||
auto last_op_as_set = dynamic_cast<SetVarElement*>(default_result.back());
|
||||
if (last_op_as_set && last_op_as_set->dst().reg() == reg) {
|
||||
default_result.pop_back();
|
||||
for (auto form : last_op_as_set->src()->elts()) {
|
||||
form->parent_form = nullptr; // will get set later, this makes it obvious if I forget.
|
||||
default_result.push_back(form);
|
||||
}
|
||||
return default_result;
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Couldn't rewrite form to get result"));
|
||||
}
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -18,6 +18,7 @@ class FormStack {
|
||||
Form* pop_reg(const Variable& var);
|
||||
bool is_single_expression();
|
||||
std::vector<FormElement*> rewrite(FormPool& pool);
|
||||
std::vector<FormElement*> rewrite_to_get_reg(FormPool& pool, Register reg);
|
||||
std::string print(const Env& env);
|
||||
|
||||
private:
|
||||
|
||||
@@ -85,4 +85,6 @@ class Variable {
|
||||
};
|
||||
|
||||
using VariableSet = std::unordered_set<Variable, Variable::hash>;
|
||||
|
||||
enum class FixedOperatorKind { GPR_TO_FPR, DIVISION, ADDITION, INVALID };
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#include "expression_build.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
#include "decompiler/IR2/FormStack.h"
|
||||
|
||||
namespace decompiler {
|
||||
bool convert_to_expressions(Form* top_level_form, FormPool& pool, const Function& f) {
|
||||
assert(top_level_form);
|
||||
|
||||
try {
|
||||
top_level_form->apply_form([&](Form* form) {
|
||||
FormStack stack;
|
||||
for (auto& entry : form->elts()) {
|
||||
entry->push_to_stack(f.ir2.env, stack);
|
||||
}
|
||||
auto new_entries = stack.rewrite(pool);
|
||||
form->clear();
|
||||
for (auto x : new_entries) {
|
||||
form->push_back(x);
|
||||
}
|
||||
});
|
||||
} catch (std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -8,10 +8,10 @@
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "decompiler/Function/TypeInspector.h"
|
||||
#include "decompiler/IR2/reg_usage.h"
|
||||
#include "decompiler/IR2/variable_naming.h"
|
||||
#include "decompiler/IR2/cfg_builder.h"
|
||||
#include "decompiler/IR2/expression_build.h"
|
||||
#include "decompiler/analysis/reg_usage.h"
|
||||
#include "decompiler/analysis/variable_naming.h"
|
||||
#include "decompiler/analysis/cfg_builder.h"
|
||||
#include "decompiler/analysis/expression_build.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
namespace decompiler {
|
||||
@@ -270,6 +270,7 @@ void ObjectFileDB::ir2_type_analysis_pass() {
|
||||
non_asm_functions++;
|
||||
TypeSpec ts;
|
||||
if (lookup_function_type(func.guessed_name, data.to_unique_name(), &ts)) {
|
||||
func.type = ts;
|
||||
attempted_functions++;
|
||||
// try type analysis here.
|
||||
auto hints = get_config().type_hints_by_function_by_idx[func.guessed_name.to_string()];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "AtomicOp.h"
|
||||
#include "decompiler/IR2/AtomicOp.h"
|
||||
|
||||
namespace decompiler {
|
||||
class Function;
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "expression_build.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
#include "decompiler/IR2/FormStack.h"
|
||||
|
||||
namespace decompiler {
|
||||
bool convert_to_expressions(Form* top_level_form, FormPool& pool, const Function& f) {
|
||||
assert(top_level_form);
|
||||
|
||||
try {
|
||||
// top_level_form->apply_form([&](Form* form) {
|
||||
// if (form == top_level_form || !form->is_single_element()) {
|
||||
// FormStack stack;
|
||||
// for (auto& entry : form->elts()) {
|
||||
// fmt::print("push {} to stack\n", entry->to_form(f.ir2.env).print());
|
||||
// entry->push_to_stack(f.ir2.env, pool, stack);
|
||||
// }
|
||||
// std::vector<FormElement*> new_entries;
|
||||
// if (form == top_level_form && f.type.last_arg() != TypeSpec("none")) {
|
||||
// new_entries = stack.rewrite_to_get_reg(pool, Register(Reg::GPR, Reg::V0));
|
||||
// } else {
|
||||
// new_entries = stack.rewrite(pool);
|
||||
// }
|
||||
// assert(!new_entries.empty());
|
||||
// form->clear();
|
||||
// for (auto x : new_entries) {
|
||||
// form->push_back(x);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
FormStack stack;
|
||||
for (auto& entry : top_level_form->elts()) {
|
||||
fmt::print("push {} to stack\n", entry->to_form(f.ir2.env).print());
|
||||
entry->push_to_stack(f.ir2.env, pool, stack);
|
||||
}
|
||||
std::vector<FormElement*> new_entries;
|
||||
if (f.type.last_arg() != TypeSpec("none")) {
|
||||
new_entries = stack.rewrite_to_get_reg(pool, Register(Reg::GPR, Reg::V0));
|
||||
} else {
|
||||
new_entries = stack.rewrite(pool);
|
||||
}
|
||||
assert(!new_entries.empty());
|
||||
top_level_form->clear();
|
||||
for (auto x : new_entries) {
|
||||
top_level_form->push_back(x);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
lg::warn("Expression building failed: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace decompiler
|
||||
+3
-1
@@ -18,8 +18,10 @@ add_executable(goalc-test
|
||||
test_pretty_print.cpp
|
||||
test_zydis.cpp
|
||||
goalc/test_goal_kernel.cpp
|
||||
decompiler/FormRegressionTest.cpp
|
||||
decompiler/test_AtomicOpBuilder.cpp
|
||||
decompiler/test_FormRegression.cpp
|
||||
decompiler/test_FormBeforeExpressions.cpp
|
||||
decompiler/test_FormExpressionBuild.cpp
|
||||
decompiler/test_InstructionParser.cpp
|
||||
${GOALC_TEST_FRAMEWORK_SOURCES}
|
||||
${GOALC_TEST_CASES})
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "FormRegressionTest.h"
|
||||
|
||||
#include "decompiler/analysis/variable_naming.h"
|
||||
#include "decompiler/analysis/reg_usage.h"
|
||||
#include "decompiler/analysis/cfg_builder.h"
|
||||
#include "decompiler/analysis/expression_build.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
using namespace decompiler;
|
||||
|
||||
void FormRegressionTest::SetUpTestCase() {
|
||||
parser = std::make_unique<InstructionParser>();
|
||||
dts = std::make_unique<DecompilerTypeSystem>();
|
||||
dts->parse_type_defs({"decompiler", "config", "all-types.gc"});
|
||||
}
|
||||
|
||||
void FormRegressionTest::TearDownTestCase() {
|
||||
parser.reset();
|
||||
dts.reset();
|
||||
parser.reset();
|
||||
}
|
||||
|
||||
void FormRegressionTest::TestData::add_string_at_label(const std::string& label_name,
|
||||
const std::string& data) {
|
||||
// first, align segment 1:
|
||||
while (file.words_by_seg.at(1).size() % 4) {
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(0));
|
||||
}
|
||||
|
||||
// add string type tag:
|
||||
LinkedWord type_tag(0);
|
||||
type_tag.kind = LinkedWord::Kind::TYPE_PTR;
|
||||
type_tag.symbol_name = "string";
|
||||
file.words_by_seg.at(1).push_back(type_tag);
|
||||
int string_start = 4 * int(file.words_by_seg.at(1).size());
|
||||
|
||||
// add size
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(int(data.length())));
|
||||
|
||||
// add string:
|
||||
std::vector<char> bytes;
|
||||
bytes.resize(((data.size() + 1 + 3) / 4) * 4);
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
bytes[i] = data[i];
|
||||
}
|
||||
for (size_t i = 0; i < bytes.size() / 4; i++) {
|
||||
auto word = ((uint32_t*)bytes.data())[i];
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(word));
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(0));
|
||||
}
|
||||
// will be already null terminated.
|
||||
|
||||
for (auto& label : file.labels) {
|
||||
if (label.name == label_name) {
|
||||
label.target_segment = 1;
|
||||
label.offset = string_start;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
|
||||
std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
bool do_expressions,
|
||||
bool allow_pairs,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings) {
|
||||
dts->type_prop_settings.locked = true;
|
||||
dts->type_prop_settings.reset();
|
||||
dts->type_prop_settings.allow_pair = allow_pairs;
|
||||
dts->type_prop_settings.current_method_type = method_name;
|
||||
auto program = parser->parse_program(code);
|
||||
// printf("prg:\n%s\n\n", program.print().c_str());
|
||||
auto test = std::make_unique<TestData>(program.instructions.size());
|
||||
test->file.words_by_seg.resize(3);
|
||||
test->file.labels = program.labels;
|
||||
test->func.ir2.env.file = &test->file;
|
||||
test->func.instructions = program.instructions;
|
||||
test->func.guessed_name.set_as_global("test-function");
|
||||
test->func.type = function_type;
|
||||
|
||||
for (auto& str : strings) {
|
||||
test->add_string_at_label(str.first, str.second);
|
||||
}
|
||||
|
||||
test->func.basic_blocks = find_blocks_in_function(test->file, 0, test->func);
|
||||
test->func.analyze_prologue(test->file);
|
||||
test->func.cfg = build_cfg(test->file, 0, test->func);
|
||||
EXPECT_TRUE(test->func.cfg->is_fully_resolved());
|
||||
|
||||
auto ops = convert_function_to_atomic_ops(test->func, program.labels);
|
||||
test->func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
|
||||
test->func.ir2.atomic_ops_succeeded = true;
|
||||
|
||||
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, {}));
|
||||
|
||||
test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func));
|
||||
|
||||
auto result = run_variable_renaming(test->func, test->func.ir2.env.reg_use(),
|
||||
*test->func.ir2.atomic_ops, *dts);
|
||||
if (result.has_value()) {
|
||||
test->func.ir2.env.set_local_vars(*result);
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
|
||||
build_initial_forms(test->func);
|
||||
EXPECT_TRUE(test->func.ir2.top_form);
|
||||
|
||||
// for now, just test that this can at least be called.
|
||||
VariableSet vars;
|
||||
test->func.ir2.top_form->collect_vars(vars);
|
||||
|
||||
if (do_expressions) {
|
||||
bool success =
|
||||
convert_to_expressions(test->func.ir2.top_form, test->func.ir2.form_pool, test->func);
|
||||
|
||||
EXPECT_TRUE(success);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
void FormRegressionTest::test(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool do_expressions,
|
||||
bool allow_pairs,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings) {
|
||||
auto ts = dts->parse_type_spec(type);
|
||||
auto test = make_function(code, ts, do_expressions, allow_pairs, method_name, strings);
|
||||
ASSERT_TRUE(test);
|
||||
auto expected_form =
|
||||
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
|
||||
auto actual_form =
|
||||
pretty_print::get_pretty_printer_reader()
|
||||
.read_from_string(test->func.ir2.top_form->to_form(test->func.ir2.env).print(), false)
|
||||
.as_pair()
|
||||
->car;
|
||||
if (expected_form != actual_form) {
|
||||
printf("Got:\n%s\n\nExpected\n%s\n", actual_form.print().c_str(),
|
||||
expected_form.print().c_str());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(expected_form == actual_form);
|
||||
}
|
||||
|
||||
std::unique_ptr<InstructionParser> FormRegressionTest::parser;
|
||||
std::unique_ptr<DecompilerTypeSystem> FormRegressionTest::dts;
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "gtest/gtest.h"
|
||||
#include "decompiler/Disasm/InstructionParser.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
class FormRegressionTest : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<decompiler::InstructionParser> parser;
|
||||
static std::unique_ptr<decompiler::DecompilerTypeSystem> dts;
|
||||
|
||||
static void SetUpTestCase();
|
||||
static void TearDownTestCase();
|
||||
|
||||
struct TestData {
|
||||
explicit TestData(int instrs) : func(0, instrs) {}
|
||||
decompiler::Function func;
|
||||
decompiler::LinkedObjectFile file;
|
||||
|
||||
void add_string_at_label(const std::string& label_name, const std::string& data);
|
||||
};
|
||||
|
||||
std::unique_ptr<TestData> make_function(
|
||||
const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
bool do_expressions,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {});
|
||||
|
||||
void test(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool do_expressions,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {});
|
||||
|
||||
void test_no_expr(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {}) {
|
||||
test(code, type, expected, false, allow_pairs, method_name, strings);
|
||||
}
|
||||
|
||||
void test_with_expr(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {}) {
|
||||
test(code, type, expected, true, allow_pairs, method_name, strings);
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "decompiler/IR2/AtomicOp.h"
|
||||
#include "decompiler/IR2/atomic_op_builder.h"
|
||||
#include "decompiler/analysis/atomic_op_builder.h"
|
||||
#include "decompiler/Disasm/InstructionParser.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
+41
-194
@@ -1,162 +1,9 @@
|
||||
#include <memory>
|
||||
#include "gtest/gtest.h"
|
||||
#include "decompiler/Disasm/InstructionParser.h"
|
||||
#include "decompiler/Disasm/DecompilerLabel.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
#include "decompiler/IR2/variable_naming.h"
|
||||
#include "decompiler/IR2/cfg_builder.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "FormRegressionTest.h"
|
||||
|
||||
using namespace decompiler;
|
||||
|
||||
class DecompilerRegressionTest : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<InstructionParser> parser;
|
||||
static std::unique_ptr<DecompilerTypeSystem> dts;
|
||||
|
||||
static void SetUpTestCase() {
|
||||
parser = std::make_unique<InstructionParser>();
|
||||
dts = std::make_unique<DecompilerTypeSystem>();
|
||||
dts->parse_type_defs({"decompiler", "config", "all-types.gc"});
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
parser.reset();
|
||||
dts.reset();
|
||||
parser.reset();
|
||||
}
|
||||
|
||||
struct TestData {
|
||||
explicit TestData(int instrs) : func(0, instrs) {}
|
||||
Function func;
|
||||
LinkedObjectFile file;
|
||||
|
||||
void add_string_at_label(const std::string& label_name, const std::string& data) {
|
||||
// first, align segment 1:
|
||||
while (file.words_by_seg.at(1).size() % 4) {
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(0));
|
||||
}
|
||||
|
||||
// add string type tag:
|
||||
LinkedWord type_tag(0);
|
||||
type_tag.kind = LinkedWord::Kind::TYPE_PTR;
|
||||
type_tag.symbol_name = "string";
|
||||
file.words_by_seg.at(1).push_back(type_tag);
|
||||
int string_start = 4 * int(file.words_by_seg.at(1).size());
|
||||
|
||||
// add size
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(int(data.length())));
|
||||
|
||||
// add string:
|
||||
std::vector<char> bytes;
|
||||
bytes.resize(((data.size() + 1 + 3) / 4) * 4);
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
bytes[i] = data[i];
|
||||
}
|
||||
for (size_t i = 0; i < bytes.size() / 4; i++) {
|
||||
auto word = ((uint32_t*)bytes.data())[i];
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(word));
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
file.words_by_seg.at(1).push_back(LinkedWord(0));
|
||||
}
|
||||
// will be already null terminated.
|
||||
|
||||
for (auto& label : file.labels) {
|
||||
if (label.name == label_name) {
|
||||
label.target_segment = 1;
|
||||
label.offset = string_start;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<TestData> make_function(
|
||||
const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {}) {
|
||||
dts->type_prop_settings.locked = true;
|
||||
dts->type_prop_settings.reset();
|
||||
dts->type_prop_settings.allow_pair = allow_pairs;
|
||||
dts->type_prop_settings.current_method_type = method_name;
|
||||
auto program = parser->parse_program(code);
|
||||
// printf("prg:\n%s\n\n", program.print().c_str());
|
||||
auto test = std::make_unique<TestData>(program.instructions.size());
|
||||
test->file.words_by_seg.resize(3);
|
||||
test->file.labels = program.labels;
|
||||
test->func.ir2.env.file = &test->file;
|
||||
test->func.instructions = program.instructions;
|
||||
test->func.guessed_name.set_as_global("test-function");
|
||||
|
||||
for (auto& str : strings) {
|
||||
test->add_string_at_label(str.first, str.second);
|
||||
}
|
||||
|
||||
test->func.basic_blocks = find_blocks_in_function(test->file, 0, test->func);
|
||||
test->func.analyze_prologue(test->file);
|
||||
test->func.cfg = build_cfg(test->file, 0, test->func);
|
||||
EXPECT_TRUE(test->func.cfg->is_fully_resolved());
|
||||
|
||||
auto ops = convert_function_to_atomic_ops(test->func, program.labels);
|
||||
test->func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
|
||||
test->func.ir2.atomic_ops_succeeded = true;
|
||||
|
||||
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, {}));
|
||||
|
||||
test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func));
|
||||
|
||||
auto result = run_variable_renaming(test->func, test->func.ir2.env.reg_use(),
|
||||
*test->func.ir2.atomic_ops, *dts);
|
||||
if (result.has_value()) {
|
||||
test->func.ir2.env.set_local_vars(*result);
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
|
||||
build_initial_forms(test->func);
|
||||
EXPECT_TRUE(test->func.ir2.top_form);
|
||||
|
||||
// for now, just test that this can at least be called.
|
||||
VariableSet vars;
|
||||
test->func.ir2.top_form->collect_vars(vars);
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
void test(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {}) {
|
||||
auto ts = dts->parse_type_spec(type);
|
||||
auto test = make_function(code, ts, allow_pairs, method_name, strings);
|
||||
auto expected_form =
|
||||
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
|
||||
auto actual_form =
|
||||
pretty_print::get_pretty_printer_reader()
|
||||
.read_from_string(test->func.ir2.top_form->to_form(test->func.ir2.env).print(), false)
|
||||
.as_pair()
|
||||
->car;
|
||||
if (expected_form != actual_form) {
|
||||
printf("Got:\n%s\n\nExpected\n%s\n", actual_form.print().c_str(),
|
||||
expected_form.print().c_str());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(expected_form == actual_form);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<InstructionParser> DecompilerRegressionTest::parser;
|
||||
std::unique_ptr<DecompilerTypeSystem> DecompilerRegressionTest::dts;
|
||||
|
||||
TEST_F(DecompilerRegressionTest, StringTest) {
|
||||
TEST_F(FormRegressionTest, StringTest) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L100:\n"
|
||||
@@ -164,7 +11,7 @@ TEST_F(DecompilerRegressionTest, StringTest) {
|
||||
"L101:\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
auto test = make_function(func, TypeSpec("function", {TypeSpec("none")}), false, "",
|
||||
auto test = make_function(func, TypeSpec("function", {TypeSpec("none")}), false, false, "",
|
||||
{{"L100", "testing-string"}, {"L101", "testing-string-2"}});
|
||||
|
||||
EXPECT_EQ(test->file.get_goal_string_by_label(test->file.get_label_by_name("L100")),
|
||||
@@ -173,7 +20,7 @@ TEST_F(DecompilerRegressionTest, StringTest) {
|
||||
"testing-string-2");
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, SimplestTest) {
|
||||
TEST_F(FormRegressionTest, SimplestTest) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" or v0, a0, r0\n"
|
||||
@@ -181,10 +28,10 @@ TEST_F(DecompilerRegressionTest, SimplestTest) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function object object)";
|
||||
std::string expected = "(set! v0-0 a0-0)";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, FloatingPointBasic) {
|
||||
TEST_F(FormRegressionTest, FloatingPointBasic) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L345:\n"
|
||||
@@ -206,10 +53,10 @@ TEST_F(DecompilerRegressionTest, FloatingPointBasic) {
|
||||
" (set! f0-1 (/.s f0-0 f1-0))\n"
|
||||
" (set! v0-0 (fpr->gpr f0-1))\n"
|
||||
" )";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Op3) {
|
||||
TEST_F(FormRegressionTest, Op3) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L308:\n"
|
||||
@@ -218,10 +65,10 @@ TEST_F(DecompilerRegressionTest, Op3) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(set! v0-0 (*.si a0-0 a1-0))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Division) {
|
||||
TEST_F(FormRegressionTest, Division) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L307:\n"
|
||||
@@ -231,10 +78,10 @@ TEST_F(DecompilerRegressionTest, Division) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(set! v0-0 (/.si a0-0 a1-0))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Ash) {
|
||||
TEST_F(FormRegressionTest, Ash) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L305:\n"
|
||||
@@ -251,10 +98,10 @@ TEST_F(DecompilerRegressionTest, Ash) {
|
||||
" sll r0, r0, 0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(begin (set! v1-0 a0-0) (set! v0-0 (ash.si v1-0 a1-0)))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Abs) {
|
||||
TEST_F(FormRegressionTest, Abs) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L301:\n"
|
||||
@@ -267,10 +114,10 @@ TEST_F(DecompilerRegressionTest, Abs) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int)";
|
||||
std::string expected = "(begin (set! v0-0 a0-0) (set! v0-1 (abs v0-0)))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Min) {
|
||||
TEST_F(FormRegressionTest, Min) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" or v0, a0, r0\n"
|
||||
@@ -281,10 +128,10 @@ TEST_F(DecompilerRegressionTest, Min) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(begin (set! v0-0 a0-0) (set! v1-0 a1-0) (set! v0-1 (min.si v0-0 v1-0)))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Max) {
|
||||
TEST_F(FormRegressionTest, Max) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L299:\n"
|
||||
@@ -296,10 +143,10 @@ TEST_F(DecompilerRegressionTest, Max) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(begin (set! v0-0 a0-0) (set! v1-0 a1-0) (set! v0-1 (max.si v0-0 v1-0)))";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, FormatString) {
|
||||
TEST_F(FormRegressionTest, FormatString) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L343:\n"
|
||||
@@ -336,10 +183,10 @@ TEST_F(DecompilerRegressionTest, FormatString) {
|
||||
" (set! v0-0 (call! a0-1 a1-0 a2-0))\n" // #t, "~f", the float
|
||||
" (set! v0-1 gp-0)\n"
|
||||
" )";
|
||||
test(func, type, expected, false, "", {{"L343", "~f"}});
|
||||
test_no_expr(func, type, expected, false, "", {{"L343", "~f"}});
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, WhileLoop) {
|
||||
TEST_F(FormRegressionTest, WhileLoop) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L285:\n"
|
||||
@@ -378,11 +225,11 @@ TEST_F(DecompilerRegressionTest, WhileLoop) {
|
||||
" )\n"
|
||||
" (set! v0-1 '#f)\n"
|
||||
" )";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
// Note - this test looks weird because or's aren't fully processed at this point.
|
||||
TEST_F(DecompilerRegressionTest, Or) {
|
||||
TEST_F(FormRegressionTest, Or) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L280:\n"
|
||||
@@ -443,10 +290,10 @@ TEST_F(DecompilerRegressionTest, Or) {
|
||||
" )\n"
|
||||
" (set! v0-1 '#f)\n"
|
||||
" )";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, DynamicMethodAccess) {
|
||||
TEST_F(FormRegressionTest, DynamicMethodAccess) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -520,10 +367,10 @@ TEST_F(DecompilerRegressionTest, DynamicMethodAccess) {
|
||||
" )\n"
|
||||
" (set! v1-5 '#f)\n"
|
||||
" )";
|
||||
test(func, type, expected);
|
||||
test_no_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, SimpleLoopMergeCheck) {
|
||||
TEST_F(FormRegressionTest, SimpleLoopMergeCheck) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -563,10 +410,10 @@ TEST_F(DecompilerRegressionTest, SimpleLoopMergeCheck) {
|
||||
" (set! v1-2 '#f)\n"
|
||||
" (set! v0-0 (l.w (+ a0-0 -2)))\n"
|
||||
" )";
|
||||
test(func, type, expected, true);
|
||||
test_no_expr(func, type, expected, true);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, And) {
|
||||
TEST_F(FormRegressionTest, And) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -632,10 +479,10 @@ TEST_F(DecompilerRegressionTest, And) {
|
||||
" (set! v1-2 '#f)\n" // while's false, I think.
|
||||
" )\n"
|
||||
" )";
|
||||
test(func, type, expected, true);
|
||||
test_no_expr(func, type, expected, true);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, FunctionCall) {
|
||||
TEST_F(FormRegressionTest, FunctionCall) {
|
||||
// nmember
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
@@ -715,10 +562,10 @@ TEST_F(DecompilerRegressionTest, FunctionCall) {
|
||||
" )\n"
|
||||
" (set! v0-2 gp-0)\n" // not empty, so return the result
|
||||
" )"; // the (set! v0 #f) from the if is added later.
|
||||
test(func, type, expected, true);
|
||||
test_no_expr(func, type, expected, true);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, NestedAndOr) {
|
||||
TEST_F(FormRegressionTest, NestedAndOr) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -886,10 +733,10 @@ TEST_F(DecompilerRegressionTest, NestedAndOr) {
|
||||
" (set! v1-12 '#f)\n"
|
||||
" (set! v0-1 gp-0)\n"
|
||||
" )";
|
||||
test(func, type, expected, true);
|
||||
test_no_expr(func, type, expected, true);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, NewMethod) {
|
||||
TEST_F(FormRegressionTest, NewMethod) {
|
||||
// inline-array-class new
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
@@ -941,10 +788,10 @@ TEST_F(DecompilerRegressionTest, NewMethod) {
|
||||
" (s.w! v0-0 gp-0)\n" // store size
|
||||
" (s.w! (+ v0-0 4) gp-0)\n"
|
||||
" )";
|
||||
test(func, type, expected, false, "inline-array-class");
|
||||
test_no_expr(func, type, expected, false, "inline-array-class");
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, Recursive) {
|
||||
TEST_F(FormRegressionTest, Recursive) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -985,10 +832,10 @@ TEST_F(DecompilerRegressionTest, Recursive) {
|
||||
" (set! v0-2 (*.si gp-0 v0-1))\n" // not quite a tail call...
|
||||
" )\n"
|
||||
" )";
|
||||
test(func, type, expected, false);
|
||||
test_no_expr(func, type, expected, false);
|
||||
}
|
||||
|
||||
TEST_F(DecompilerRegressionTest, TypeOf) {
|
||||
TEST_F(FormRegressionTest, TypeOf) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
@@ -1020,5 +867,5 @@ TEST_F(DecompilerRegressionTest, TypeOf) {
|
||||
" (set! t9-0 (l.wu (+ v1-1 24)))\n" // print method.
|
||||
" (set! v0-0 (call! a0-0))\n"
|
||||
" )";
|
||||
test(func, type, expected, false);
|
||||
test_no_expr(func, type, expected, false);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "FormRegressionTest.h"
|
||||
|
||||
using namespace decompiler;
|
||||
|
||||
TEST_F(FormRegressionTest, ExprIdentity) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" or v0, a0, r0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function object object)";
|
||||
std::string expected = "a0-0";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprFloatingPoint) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L345:\n"
|
||||
" daddiu sp, sp, -16\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" lwc1 f0, L345(fp)\n"
|
||||
" mtc1 f1, a0\n"
|
||||
" div.s f0, f0, f1\n"
|
||||
" mfc1 v0, f0\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 16";
|
||||
std::string type = "(function float float)";
|
||||
std::string expected = "(/ (l.f L345) a0-0)";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, AdditionSigned) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddu v0, a0, a1\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int int)";
|
||||
std::string expected = "(+ a0-0 a1-0)";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, AdditionUnSigned) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddu v0, a0, a1\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function uint uint uint)";
|
||||
std::string expected = "(+ a0-0 a1-0)";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, AdditionMixed1) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddu v0, a0, a1\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int uint int)";
|
||||
std::string expected = "(+ a0-0 (the-as int a1-0))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, AdditionMixed2) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddu v0, a0, a1\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function uint int uint)";
|
||||
std::string expected = "(+ a0-0 (the-as uint a1-0))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
// TODO - test the additions, but with the wrong return types.
|
||||
@@ -2,15 +2,5 @@
|
||||
#include "test_collections.cpp"
|
||||
#include "test_compiler.cpp"
|
||||
#include "test_control_statements.cpp"
|
||||
#include "test_float.cpp"
|
||||
#include "test_functions.cpp"
|
||||
#include "test_library.cpp"
|
||||
#include "test_logic.cpp"
|
||||
#include "test_loop_recur.cpp"
|
||||
#include "test_macros.cpp"
|
||||
#include "test_methods.cpp"
|
||||
#include "test_pointers.cpp"
|
||||
#include "test_strings.cpp"
|
||||
#include "test_symbols.cpp"
|
||||
#include "test_variables.cpp"
|
||||
#include "test_with_game.cpp"
|
||||
|
||||
@@ -121,15 +121,21 @@ class ArithmeticTests : public testing::TestWithParam<IntegerParam> {
|
||||
// Per-test-suite set-up.
|
||||
// Called before the first test in this test suite.
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
runtime_thread = std::make_unique<std::thread>(std::thread((GoalTest::runtime_no_kernel)));
|
||||
compiler = std::make_unique<Compiler>();
|
||||
runner = std::make_unique<GoalTest::CompilerTestRunner>();
|
||||
runner->c = compiler.get();
|
||||
}
|
||||
|
||||
// Per-test-suite tear-down.
|
||||
// Called after the last test in this test suite.
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
compiler->shutdown_target();
|
||||
runtime_thread->join();
|
||||
|
||||
runtime_thread.reset();
|
||||
compiler.reset();
|
||||
runner.reset();
|
||||
}
|
||||
|
||||
// You can define per-test set-up logic as usual.
|
||||
@@ -142,9 +148,9 @@ class ArithmeticTests : public testing::TestWithParam<IntegerParam> {
|
||||
void TearDown() {}
|
||||
|
||||
// Common Resources Across all Tests in the Suite
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
static std::unique_ptr<std::thread> runtime_thread;
|
||||
static std::unique_ptr<Compiler> compiler;
|
||||
static std::unique_ptr<GoalTest::CompilerTestRunner> runner;
|
||||
|
||||
// Just to promote better test organization, supports nesting the test files 1 directory deep
|
||||
std::string testCategory = "arithmetic";
|
||||
@@ -154,9 +160,9 @@ class ArithmeticTests : public testing::TestWithParam<IntegerParam> {
|
||||
|
||||
// You must initialize the static variables outside of the declaration, or you'll run into
|
||||
// unresolved external errors
|
||||
std::thread ArithmeticTests::runtime_thread;
|
||||
Compiler ArithmeticTests::compiler;
|
||||
GoalTest::CompilerTestRunner ArithmeticTests::runner;
|
||||
std::unique_ptr<std::thread> ArithmeticTests::runtime_thread;
|
||||
std::unique_ptr<Compiler> ArithmeticTests::compiler;
|
||||
std::unique_ptr<GoalTest::CompilerTestRunner> ArithmeticTests::runner;
|
||||
|
||||
// Finally, we define our generic test, given our custom class that represents our test inputs
|
||||
// we can generate the lisp file, and pass along the path to the test runner
|
||||
@@ -169,7 +175,7 @@ TEST_P(ArithmeticTests, EvalIntegers) {
|
||||
|
||||
std::string testFile = "eval-integer-" + std::to_string(param.index) + ".generated.gc";
|
||||
env.write("eval-integer.template.gc", data, testFile);
|
||||
runner.run_test(testCategory, testFile, {param.eval()});
|
||||
runner->run_test(testCategory, testFile, {param.eval()});
|
||||
}
|
||||
|
||||
// ValuesIn, is not the only way to use a parameterized test, but the most applicable for this
|
||||
@@ -184,65 +190,102 @@ INSTANTIATE_TEST_SUITE_P(EvalIntegers,
|
||||
IntegerParam(0), IntegerParam(-0)})));
|
||||
|
||||
TEST_F(ArithmeticTests, Addition) {
|
||||
runner.run_static_test(env, testCategory, "add-int-literals.static.gc", {"13\n"});
|
||||
runner.run_static_test(env, testCategory, "add-let.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "add-int-literals.static.gc", {"13\n"});
|
||||
runner->run_static_test(env, testCategory, "add-let.static.gc", {"7\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, AddIntegerFunction) {
|
||||
runner.run_static_test(env, testCategory, "add-function.static.gc", {"21\n"});
|
||||
runner->run_static_test(env, testCategory, "add-function.static.gc", {"21\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, AddIntegerMultiple) {
|
||||
runner.run_static_test(env, testCategory, "add-int-multiple.static.gc", {"15\n"});
|
||||
runner.run_static_test(env, testCategory, "add-int-multiple-2.static.gc", {"15\n"});
|
||||
runner->run_static_test(env, testCategory, "add-int-multiple.static.gc", {"15\n"});
|
||||
runner->run_static_test(env, testCategory, "add-int-multiple-2.static.gc", {"15\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, AddIntegerVariables) {
|
||||
runner.run_static_test(env, testCategory, "add-int-vars.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "add-int-vars.static.gc", {"7\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, AshFunction) {
|
||||
runner.run_static_test(env, testCategory, "ash.static.gc", {"18\n"});
|
||||
runner->run_static_test(env, testCategory, "ash.static.gc", {"18\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Division) {
|
||||
runner.run_static_test(env, testCategory, "divide-1.static.gc", {"6\n"});
|
||||
runner.run_static_test(env, testCategory, "divide-2.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "divide-1.static.gc", {"6\n"});
|
||||
runner->run_static_test(env, testCategory, "divide-2.static.gc", {"7\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, IntegerSymbol) {
|
||||
runner.run_static_test(env, testCategory, "negative-int-symbol.static.gc", {"-123\n"});
|
||||
runner->run_static_test(env, testCategory, "negative-int-symbol.static.gc", {"-123\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Modulus) {
|
||||
runner.run_static_test(env, testCategory, "mod.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "mod.static.gc", {"7\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Multiplication) {
|
||||
runner.run_static_test(env, testCategory, "multiply.static.gc", {"-12\n"});
|
||||
runner.run_static_test(env, testCategory, "multiply-let.static.gc", {"3\n"});
|
||||
runner->run_static_test(env, testCategory, "multiply.static.gc", {"-12\n"});
|
||||
runner->run_static_test(env, testCategory, "multiply-let.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, NestedFunctionCall) {
|
||||
runner.run_static_test(env, testCategory, "nested-function.static.gc", {"10\n"});
|
||||
runner->run_static_test(env, testCategory, "nested-function.static.gc", {"10\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, VariableShift) {
|
||||
runner.run_static_test(env, testCategory, "shiftvs.static.gc", {"11\n"});
|
||||
runner->run_static_test(env, testCategory, "shiftvs.static.gc", {"11\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, FixedShift) {
|
||||
// same math as the variable shift test, just using the fixed shift operators.
|
||||
runner.run_static_test(env, testCategory, "shift-fixed.static.gc", {"11\n"});
|
||||
runner->run_static_test(env, testCategory, "shift-fixed.static.gc", {"11\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Subtraction) {
|
||||
runner.run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"});
|
||||
runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"});
|
||||
runner.run_static_test(env, testCategory, "subtract-let.static.gc", {"3\n"});
|
||||
runner->run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"});
|
||||
runner->run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"});
|
||||
runner->run_static_test(env, testCategory, "subtract-let.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Multiplication2) {
|
||||
runner.run_static_test(env, testCategory, "multiply32.static.gc", {"-1234478448\n"});
|
||||
runner.run_static_test(env, testCategory, "multiply64.static.gc", {"93270638141856400\n"});
|
||||
}
|
||||
runner->run_static_test(env, testCategory, "multiply32.static.gc", {"-1234478448\n"});
|
||||
runner->run_static_test(env, testCategory, "multiply64.static.gc", {"93270638141856400\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Constants) {
|
||||
runner->run_static_test(env, testCategory, "float.static.gc", {"1067316150\n"});
|
||||
runner->run_static_test(env, testCategory, "function-return-float-constant.static.gc",
|
||||
{"3.14149\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Operations) {
|
||||
runner->run_static_test(env, testCategory, "float-pow.static.gc", {"256\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "float-product.static.gc", {"120.0000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Symbols) {
|
||||
runner->run_static_test(env, testCategory, "float-in-symbol.static.gc", {"2345.6000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Functions) {
|
||||
runner->run_static_test(env, testCategory, "float-function.static.gc", {"10.152\n0\n"});
|
||||
runner->run_static_test(
|
||||
env, testCategory, "nested-float-functions.static.gc",
|
||||
{"i 1.4400 3.4000\nr 10.1523\ni 1.2000 10.1523\nr 17.5432\n17.543 10.152\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, MinMax) {
|
||||
runner->run_static_test(env, testCategory, "float-max.static.gc", {"3.70\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "float-min.static.gc", {"-1.20\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, LogicalOperators) {
|
||||
runner->run_static_test(env, testCategory, "logand.static.gc", {"4\n"});
|
||||
runner->run_static_test(env, testCategory, "logior.static.gc", {"60\n"});
|
||||
runner->run_static_test(env, testCategory, "logxor.static.gc", {"56\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Comparison) {
|
||||
runner->run_static_test(env, testCategory, "signed-int-compare.static.gc", {"12\n"});
|
||||
}
|
||||
|
||||
@@ -25,13 +25,19 @@ struct CollectionParam {
|
||||
class CollectionTests : public testing::TestWithParam<CollectionParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
runtime_thread = std::make_unique<std::thread>(std::thread((GoalTest::runtime_no_kernel)));
|
||||
compiler = std::make_unique<Compiler>();
|
||||
runner = std::make_unique<GoalTest::CompilerTestRunner>();
|
||||
runner->c = compiler.get();
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
compiler->shutdown_target();
|
||||
runtime_thread->join();
|
||||
|
||||
runtime_thread.reset();
|
||||
compiler.reset();
|
||||
runner.reset();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
@@ -41,36 +47,36 @@ class CollectionTests : public testing::TestWithParam<CollectionParam> {
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
static std::unique_ptr<std::thread> runtime_thread;
|
||||
static std::unique_ptr<Compiler> compiler;
|
||||
static std::unique_ptr<GoalTest::CompilerTestRunner> runner;
|
||||
|
||||
std::string testCategory = "collections";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread CollectionTests::runtime_thread;
|
||||
Compiler CollectionTests::compiler;
|
||||
GoalTest::CompilerTestRunner CollectionTests::runner;
|
||||
std::unique_ptr<std::thread> CollectionTests::runtime_thread;
|
||||
std::unique_ptr<Compiler> CollectionTests::compiler;
|
||||
std::unique_ptr<GoalTest::CompilerTestRunner> CollectionTests::runner;
|
||||
|
||||
TEST_F(CollectionTests, Pairs) {
|
||||
runner.run_static_test(env, testCategory, "empty-pair.static.gc", {"()\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "pair-check.static.gc", {"#t#f\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "empty-pair.static.gc", {"()\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "pair-check.static.gc", {"#t#f\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(CollectionTests, Lists) {
|
||||
runner.run_static_test(env, testCategory, "list.static.gc", {"(a b c d)\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "list.static.gc", {"(a b c d)\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(CollectionTests, InlineArray) {
|
||||
runner.run_static_test(env, testCategory, "inline-array-field.static.gc", {"16\n"});
|
||||
runner->run_static_test(env, testCategory, "inline-array-field.static.gc", {"16\n"});
|
||||
}
|
||||
|
||||
TEST_F(CollectionTests, Operations) {
|
||||
runner.run_static_test(env, testCategory, "cons.static.gc", {"(a . b)\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "car-cdr-get.static.gc", {"ab\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "car-cdr-set.static.gc", {"(c . d)\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "nested-car-cdr-set.static.gc",
|
||||
{"efgh\n((e . g) f . h)\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "cons.static.gc", {"(a . b)\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "car-cdr-get.static.gc", {"ab\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "car-cdr-set.static.gc", {"(c . d)\n0\n"});
|
||||
runner->run_static_test(env, testCategory, "nested-car-cdr-set.static.gc",
|
||||
{"efgh\n((e . g) f . h)\n0\n"});
|
||||
}
|
||||
|
||||
@@ -25,13 +25,19 @@ struct ControlStatementParam {
|
||||
class ControlStatementTests : public testing::TestWithParam<ControlStatementParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
runtime_thread = std::make_unique<std::thread>(std::thread((GoalTest::runtime_no_kernel)));
|
||||
compiler = std::make_unique<Compiler>();
|
||||
runner = std::make_unique<GoalTest::CompilerTestRunner>();
|
||||
runner->c = compiler.get();
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
compiler->shutdown_target();
|
||||
runtime_thread->join();
|
||||
|
||||
runtime_thread.reset();
|
||||
compiler.reset();
|
||||
runner.reset();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
@@ -41,33 +47,152 @@ class ControlStatementTests : public testing::TestWithParam<ControlStatementPara
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
static std::unique_ptr<std::thread> runtime_thread;
|
||||
static std::unique_ptr<Compiler> compiler;
|
||||
static std::unique_ptr<GoalTest::CompilerTestRunner> runner;
|
||||
|
||||
std::string testCategory = "control-statements";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread ControlStatementTests::runtime_thread;
|
||||
Compiler ControlStatementTests::compiler;
|
||||
GoalTest::CompilerTestRunner ControlStatementTests::runner;
|
||||
std::unique_ptr<std::thread> ControlStatementTests::runtime_thread;
|
||||
std::unique_ptr<Compiler> ControlStatementTests::compiler;
|
||||
std::unique_ptr<GoalTest::CompilerTestRunner> ControlStatementTests::runner;
|
||||
|
||||
TEST_F(ControlStatementTests, ConditionalCompilation) {
|
||||
runner.run_static_test(env, testCategory, "conditional-compilation.static.gc", {"3\n"});
|
||||
runner->run_static_test(env, testCategory, "conditional-compilation.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Blocks) {
|
||||
runner.run_static_test(env, testCategory, "nested-blocks-1.static.gc", {"7\n"});
|
||||
runner.run_static_test(env, testCategory, "nested-blocks-2.static.gc", {"8\n"});
|
||||
runner.run_static_test(env, testCategory, "nested-blocks-3.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "nested-blocks-1.static.gc", {"7\n"});
|
||||
runner->run_static_test(env, testCategory, "nested-blocks-2.static.gc", {"8\n"});
|
||||
runner->run_static_test(env, testCategory, "nested-blocks-3.static.gc", {"7\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, GoTo) {
|
||||
runner.run_static_test(env, testCategory, "goto.static.gc", {"3\n"});
|
||||
runner->run_static_test(env, testCategory, "goto.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Branch) {
|
||||
runner.run_static_test(env, testCategory, "return-value-of-if.static.gc", {"123\n"});
|
||||
runner->run_static_test(env, testCategory, "return-value-of-if.static.gc", {"123\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, DoTimes) {
|
||||
runner->run_static_test(env, testCategory, "dotimes.static.gc", {"4950\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Factorial) {
|
||||
runner->run_static_test(env, testCategory, "factorial-recursive.static.gc", {"3628800\n"});
|
||||
runner->run_static_test(env, testCategory, "factorial-iterative.static.gc", {"3628800\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Definitions) {
|
||||
runner->run_static_test(env, testCategory, "defun-return-constant.static.gc", {"12\n"});
|
||||
runner->run_static_test(env, testCategory, "defun-return-symbol.static.gc", {"42\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, ReturnValue) {
|
||||
runner->run_static_test(env, testCategory, "return.static.gc", {"77\n"});
|
||||
runner->run_static_test(env, testCategory, "return-arg.static.gc", {"23\n"});
|
||||
runner->run_static_test(env, testCategory, "return-colors.static.gc", {"77\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Calling) {
|
||||
runner->run_static_test(env, testCategory, "nested-call.static.gc", {"2\n"});
|
||||
runner->run_static_test(env, testCategory, "inline-call.static.gc", {"44\n"});
|
||||
runner->run_static_test(env, testCategory, "simple-call.static.gc", {"30\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Anonymous) {
|
||||
runner->run_static_test(env, testCategory, "declare-inline.static.gc", {"32\n"});
|
||||
runner->run_static_test(env, testCategory, "lambda-1.static.gc", {"2\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, InlineIsInline) {
|
||||
auto code = compiler->get_goos().reader.read_from_file(
|
||||
{"test/goalc/source_templates/control-statements/declare-inline.static.gc"});
|
||||
auto compiled = compiler->compile_object_file("test-code", code, true);
|
||||
EXPECT_EQ(compiled->functions().size(), 2);
|
||||
auto& ir = compiled->top_level_function().code();
|
||||
bool got_mult = false;
|
||||
for (auto& x : ir) {
|
||||
EXPECT_EQ(dynamic_cast<IR_FunctionCall*>(x.get()), nullptr);
|
||||
auto as_im = dynamic_cast<IR_IntegerMath*>(x.get());
|
||||
if (as_im) {
|
||||
EXPECT_EQ(as_im->get_kind(), IntegerMathKind::IMUL_32);
|
||||
got_mult = true;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(got_mult);
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, AllowInline) {
|
||||
auto code = compiler->get_goos().reader.read_from_file(
|
||||
{"test/goalc/source_templates/control-statements/inline-call.static.gc"});
|
||||
auto compiled = compiler->compile_object_file("test-code", code, true);
|
||||
EXPECT_EQ(compiled->functions().size(), 2);
|
||||
auto& ir = compiled->top_level_function().code();
|
||||
int got_mult = 0;
|
||||
int got_call = 0;
|
||||
for (auto& x : ir) {
|
||||
if (dynamic_cast<IR_FunctionCall*>(x.get())) {
|
||||
got_call++;
|
||||
}
|
||||
auto as_im = dynamic_cast<IR_IntegerMath*>(x.get());
|
||||
if (as_im && as_im->get_kind() == IntegerMathKind::IMUL_32) {
|
||||
got_mult++;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(got_mult, 1);
|
||||
EXPECT_EQ(got_call, 1);
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, ReturnNone) {
|
||||
runner->run_static_test(env, testCategory, "function-returning-none.static.gc", {"1\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, InlineBlock1) {
|
||||
runner->run_static_test(env, testCategory, "inline-with-block-1.static.gc", {"1\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, InlineBlock2) {
|
||||
runner->run_static_test(env, testCategory, "inline-with-block-2.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, InlineBlock3) {
|
||||
runner->run_static_test(env, testCategory, "inline-with-block-3.static.gc", {"4\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, InlineBlock4) {
|
||||
runner->run_static_test(env, testCategory, "inline-with-block-4.static.gc", {"3.0000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, ReturnFromTrick) {
|
||||
runner->run_static_test(env, testCategory, "return-from-trick.static.gc", {"1\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Set) {
|
||||
runner->run_static_test(env, testCategory, "set-symbol.static.gc", {"22\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Protect) {
|
||||
runner->run_static_test(env, testCategory, "protect.static.gc", {"33\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Align) {
|
||||
runner->run_static_test(env, testCategory, "align16-1.static.gc", {"80\n"});
|
||||
runner->run_static_test(env, testCategory, "align16-2.static.gc", {"64\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Defsmacro) {
|
||||
runner->run_static_test(env, testCategory, "defsmacro-defgmacro.static.gc", {"20\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, Desfun) {
|
||||
runner->run_static_test(env, testCategory, "desfun.static.gc", {"4\n"});
|
||||
}
|
||||
|
||||
TEST_F(ControlStatementTests, DeReference) {
|
||||
runner->run_static_test(env, testCategory, "methods.static.gc", {"#t#t\n0\n"});
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct FloatParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class FloatTests : public testing::TestWithParam<FloatParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "float";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread FloatTests::runtime_thread;
|
||||
Compiler FloatTests::compiler;
|
||||
GoalTest::CompilerTestRunner FloatTests::runner;
|
||||
|
||||
TEST_F(FloatTests, Constants) {
|
||||
runner.run_static_test(env, testCategory, "float.static.gc", {"1067316150\n"});
|
||||
runner.run_static_test(env, testCategory, "function-return-float-constant.static.gc",
|
||||
{"3.14149\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(FloatTests, Operations) {
|
||||
runner.run_static_test(env, testCategory, "float-pow.static.gc", {"256\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "float-product.static.gc", {"120.0000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(FloatTests, Symbols) {
|
||||
runner.run_static_test(env, testCategory, "float-in-symbol.static.gc", {"2345.6000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(FloatTests, Functions) {
|
||||
runner.run_static_test(env, testCategory, "float-function.static.gc", {"10.152\n0\n"});
|
||||
runner.run_static_test(
|
||||
env, testCategory, "nested-float-functions.static.gc",
|
||||
{"i 1.4400 3.4000\nr 10.1523\ni 1.2000 10.1523\nr 17.5432\n17.543 10.152\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(FloatTests, MinMax) {
|
||||
runner.run_static_test(env, testCategory, "float-max.static.gc", {"3.70\n0\n"});
|
||||
runner.run_static_test(env, testCategory, "float-min.static.gc", {"-1.20\n0\n"});
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct FunctionParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class FunctionTests : public testing::TestWithParam<FunctionParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "functions";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread FunctionTests::runtime_thread;
|
||||
Compiler FunctionTests::compiler;
|
||||
GoalTest::CompilerTestRunner FunctionTests::runner;
|
||||
|
||||
TEST_F(FunctionTests, Definitions) {
|
||||
runner.run_static_test(env, testCategory, "defun-return-constant.static.gc", {"12\n"});
|
||||
runner.run_static_test(env, testCategory, "defun-return-symbol.static.gc", {"42\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, ReturnValue) {
|
||||
runner.run_static_test(env, testCategory, "return.static.gc", {"77\n"});
|
||||
runner.run_static_test(env, testCategory, "return-arg.static.gc", {"23\n"});
|
||||
runner.run_static_test(env, testCategory, "return-colors.static.gc", {"77\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, Calling) {
|
||||
runner.run_static_test(env, testCategory, "nested-call.static.gc", {"2\n"});
|
||||
runner.run_static_test(env, testCategory, "inline-call.static.gc", {"44\n"});
|
||||
runner.run_static_test(env, testCategory, "simple-call.static.gc", {"30\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, Anonymous) {
|
||||
runner.run_static_test(env, testCategory, "declare-inline.static.gc", {"32\n"});
|
||||
runner.run_static_test(env, testCategory, "lambda-1.static.gc", {"2\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, InlineIsInline) {
|
||||
auto code = compiler.get_goos().reader.read_from_file(
|
||||
{"test/goalc/source_templates/functions/declare-inline.static.gc"});
|
||||
auto compiled = compiler.compile_object_file("test-code", code, true);
|
||||
EXPECT_EQ(compiled->functions().size(), 2);
|
||||
auto& ir = compiled->top_level_function().code();
|
||||
bool got_mult = false;
|
||||
for (auto& x : ir) {
|
||||
EXPECT_EQ(dynamic_cast<IR_FunctionCall*>(x.get()), nullptr);
|
||||
auto as_im = dynamic_cast<IR_IntegerMath*>(x.get());
|
||||
if (as_im) {
|
||||
EXPECT_EQ(as_im->get_kind(), IntegerMathKind::IMUL_32);
|
||||
got_mult = true;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(got_mult);
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, AllowInline) {
|
||||
auto code = compiler.get_goos().reader.read_from_file(
|
||||
{"test/goalc/source_templates/functions/inline-call.static.gc"});
|
||||
auto compiled = compiler.compile_object_file("test-code", code, true);
|
||||
EXPECT_EQ(compiled->functions().size(), 2);
|
||||
auto& ir = compiled->top_level_function().code();
|
||||
int got_mult = 0;
|
||||
int got_call = 0;
|
||||
for (auto& x : ir) {
|
||||
if (dynamic_cast<IR_FunctionCall*>(x.get())) {
|
||||
got_call++;
|
||||
}
|
||||
auto as_im = dynamic_cast<IR_IntegerMath*>(x.get());
|
||||
if (as_im && as_im->get_kind() == IntegerMathKind::IMUL_32) {
|
||||
got_mult++;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(got_mult, 1);
|
||||
EXPECT_EQ(got_call, 1);
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, ReturnNone) {
|
||||
runner.run_static_test(env, testCategory, "function-returning-none.static.gc", {"1\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, InlineBlock1) {
|
||||
runner.run_static_test(env, testCategory, "inline-with-block-1.static.gc", {"1\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, InlineBlock2) {
|
||||
runner.run_static_test(env, testCategory, "inline-with-block-2.static.gc", {"3\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, InlineBlock3) {
|
||||
runner.run_static_test(env, testCategory, "inline-with-block-3.static.gc", {"4\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, InlineBlock4) {
|
||||
runner.run_static_test(env, testCategory, "inline-with-block-4.static.gc", {"3.0000\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(FunctionTests, ReturnFromTrick) {
|
||||
runner.run_static_test(env, testCategory, "return-from-trick.static.gc", {"1\n"});
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct LibraryParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class LibraryTests : public testing::TestWithParam<LibraryParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "library";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread LibraryTests::runtime_thread;
|
||||
Compiler LibraryTests::compiler;
|
||||
GoalTest::CompilerTestRunner LibraryTests::runner;
|
||||
|
||||
TEST_F(LibraryTests, Set) {
|
||||
runner.run_static_test(env, testCategory, "set-symbol.static.gc", {"22\n"});
|
||||
}
|
||||
|
||||
TEST_F(LibraryTests, Protect) {
|
||||
runner.run_static_test(env, testCategory, "protect.static.gc", {"33\n"});
|
||||
}
|
||||
|
||||
TEST_F(LibraryTests, Align) {
|
||||
runner.run_static_test(env, testCategory, "align16-1.static.gc", {"80\n"});
|
||||
runner.run_static_test(env, testCategory, "align16-2.static.gc", {"64\n"});
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct LogicParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class LogicTests : public testing::TestWithParam<LogicParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "logic";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread LogicTests::runtime_thread;
|
||||
Compiler LogicTests::compiler;
|
||||
GoalTest::CompilerTestRunner LogicTests::runner;
|
||||
|
||||
TEST_F(LogicTests, LogicalOperators) {
|
||||
runner.run_static_test(env, testCategory, "logand.static.gc", {"4\n"});
|
||||
runner.run_static_test(env, testCategory, "logior.static.gc", {"60\n"});
|
||||
runner.run_static_test(env, testCategory, "logxor.static.gc", {"56\n"});
|
||||
}
|
||||
|
||||
TEST_F(LogicTests, Comparison) {
|
||||
runner.run_static_test(env, testCategory, "signed-int-compare.static.gc", {"12\n"});
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct LoopRecurParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class LoopRecurTests : public testing::TestWithParam<LoopRecurParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "loop_recur";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread LoopRecurTests::runtime_thread;
|
||||
Compiler LoopRecurTests::compiler;
|
||||
GoalTest::CompilerTestRunner LoopRecurTests::runner;
|
||||
|
||||
TEST_F(LoopRecurTests, DoTimes) {
|
||||
runner.run_static_test(env, testCategory, "dotimes.static.gc", {"4950\n"});
|
||||
}
|
||||
|
||||
TEST_F(LoopRecurTests, Factorial) {
|
||||
runner.run_static_test(env, testCategory, "factorial-recursive.static.gc", {"3628800\n"});
|
||||
runner.run_static_test(env, testCategory, "factorial-iterative.static.gc", {"3628800\n"});
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct MacroParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class MacroTests : public testing::TestWithParam<MacroParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "macros";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread MacroTests::runtime_thread;
|
||||
Compiler MacroTests::compiler;
|
||||
GoalTest::CompilerTestRunner MacroTests::runner;
|
||||
|
||||
TEST_F(MacroTests, Defsmacro) {
|
||||
runner.run_static_test(env, testCategory, "defsmacro-defgmacro.static.gc", {"20\n"});
|
||||
}
|
||||
|
||||
TEST_F(MacroTests, Desfun) {
|
||||
runner.run_static_test(env, testCategory, "desfun.static.gc", {"4\n"});
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct MethodParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class MethodTests : public testing::TestWithParam<MethodParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "methods";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread MethodTests::runtime_thread;
|
||||
Compiler MethodTests::compiler;
|
||||
GoalTest::CompilerTestRunner MethodTests::runner;
|
||||
|
||||
TEST_F(MethodTests, DeReference) {
|
||||
runner.run_static_test(env, testCategory, "methods.static.gc", {"#t#t\n0\n"});
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct PointerParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class PointerTests : public testing::TestWithParam<PointerParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "pointers";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread PointerTests::runtime_thread;
|
||||
Compiler PointerTests::compiler;
|
||||
GoalTest::CompilerTestRunner PointerTests::runner;
|
||||
|
||||
TEST_F(PointerTests, DeReference) {
|
||||
runner.run_static_test(env, testCategory, "deref-simple.static.gc", {"structure\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(PointerTests, Pointers) {
|
||||
runner.run_static_test(env, testCategory, "pointers.static.gc", {"13\n"});
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct StringParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class StringTests : public testing::TestWithParam<StringParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "strings";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread StringTests::runtime_thread;
|
||||
Compiler StringTests::compiler;
|
||||
GoalTest::CompilerTestRunner StringTests::runner;
|
||||
|
||||
TEST_F(StringTests, Constants) {
|
||||
// TODO - runner.run_static_test(env, testCategory, "string-constant-1.static.gc");
|
||||
std::string expected = "\"test string!\"";
|
||||
runner.run_static_test(env, testCategory, "string-constant-2.static.gc", {expected},
|
||||
expected.size());
|
||||
}
|
||||
|
||||
TEST_F(StringTests, Symbols) {
|
||||
runner.run_static_test(env, testCategory, "quote-symbol.static.gc", {"banana\n0\n"});
|
||||
std::string expected = "test-string";
|
||||
runner.run_static_test(env, testCategory, "string-symbol.static.gc", {expected}, expected.size());
|
||||
}
|
||||
|
||||
TEST_F(StringTests, Formatting) {
|
||||
runner.run_static_test(env, testCategory, "format-reg-order.static.gc",
|
||||
{"test 1 2 3 4 5 6\n0\n"});
|
||||
}
|
||||
|
||||
// expected =
|
||||
// "test newline\nnewline\ntest tilde ~ \ntest A print boxed-string: \"boxed string!\"\ntest
|
||||
// A " "print symbol: a-symbol\ntest A make boxed object longer: \"srt\"!\ntest A
|
||||
// " "non-default pad: zzzzzzpad-me\ntest A shorten(4): a23~\ntest A don'tchange(4):
|
||||
// a234\ntest A " "shorten with pad(4): sho~\ntest A a few things \"one thing\" a-second
|
||||
// integer #<compiled " "function @ #x161544>\n";
|
||||
//
|
||||
// expected += "test S a string a-symbol another string!\n";
|
||||
// expected += "test C ) ]\n";
|
||||
// expected += "test P (no type) #<compiled function @ #x161544>\n";
|
||||
// expected += "test P (with type) 1447236\n";
|
||||
//
|
||||
// // todo, finish format testing.
|
||||
// runner.run_test_from_file("test-format.gc", {expected}, expected.size());
|
||||
@@ -1,62 +0,0 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "game/runtime.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "inja.hpp"
|
||||
#include "third-party/json.hpp"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
struct SymbolParam {
|
||||
// TODO - Not Needed Yet
|
||||
};
|
||||
|
||||
class SymbolTests : public testing::TestWithParam<SymbolParam> {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
||||
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
||||
}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
|
||||
std::string testCategory = "symbols";
|
||||
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
||||
GoalTest::getGeneratedDir(testCategory)};
|
||||
};
|
||||
|
||||
std::thread SymbolTests::runtime_thread;
|
||||
Compiler SymbolTests::compiler;
|
||||
GoalTest::CompilerTestRunner SymbolTests::runner;
|
||||
|
||||
TEST_F(SymbolTests, GetSymbol) {
|
||||
runner.run_static_test(env, testCategory, "get-symbol-1.static.gc",
|
||||
{"1342756\n"}); // 0x147d24 in hex
|
||||
runner.run_static_test(env, testCategory, "get-symbol-2.static.gc",
|
||||
{"1342764\n"}); // 0x147d2c in hex
|
||||
}
|
||||
@@ -93,4 +93,52 @@ TEST_F(VariableTests, StackArrayAlignment) {
|
||||
|
||||
TEST_F(VariableTests, StackStructureAlignment) {
|
||||
runner.run_static_test(env, testCategory, "stack-structure-align.gc", {"1234\n"});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, GetSymbol) {
|
||||
runner.run_static_test(env, testCategory, "get-symbol-1.static.gc",
|
||||
{"1342756\n"}); // 0x147d24 in hex
|
||||
runner.run_static_test(env, testCategory, "get-symbol-2.static.gc",
|
||||
{"1342764\n"}); // 0x147d2c in hex
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, Constants) {
|
||||
// TODO - runner.run_static_test(env, testCategory, "string-constant-1.static.gc");
|
||||
std::string expected = "\"test string!\"";
|
||||
runner.run_static_test(env, testCategory, "string-constant-2.static.gc", {expected},
|
||||
expected.size());
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, Symbols) {
|
||||
runner.run_static_test(env, testCategory, "quote-symbol.static.gc", {"banana\n0\n"});
|
||||
std::string expected = "test-string";
|
||||
runner.run_static_test(env, testCategory, "string-symbol.static.gc", {expected}, expected.size());
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, Formatting) {
|
||||
runner.run_static_test(env, testCategory, "format-reg-order.static.gc",
|
||||
{"test 1 2 3 4 5 6\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, DeReference) {
|
||||
runner.run_static_test(env, testCategory, "deref-simple.static.gc", {"structure\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, Pointers) {
|
||||
runner.run_static_test(env, testCategory, "pointers.static.gc", {"13\n"});
|
||||
}
|
||||
|
||||
// expected =
|
||||
// "test newline\nnewline\ntest tilde ~ \ntest A print boxed-string: \"boxed string!\"\ntest
|
||||
// A " "print symbol: a-symbol\ntest A make boxed object longer: \"srt\"!\ntest A
|
||||
// " "non-default pad: zzzzzzpad-me\ntest A shorten(4): a23~\ntest A don'tchange(4):
|
||||
// a234\ntest A " "shorten with pad(4): sho~\ntest A a few things \"one thing\" a-second
|
||||
// integer #<compiled " "function @ #x161544>\n";
|
||||
//
|
||||
// expected += "test S a string a-symbol another string!\n";
|
||||
// expected += "test C ) ]\n";
|
||||
// expected += "test P (no type) #<compiled function @ #x161544>\n";
|
||||
// expected += "test P (with type) 1447236\n";
|
||||
//
|
||||
// // todo, finish format testing.
|
||||
// runner.run_test_from_file("test-format.gc", {expected}, expected.size());
|
||||
|
||||
@@ -58,7 +58,7 @@ TEST(Listener, CheckConnectionStaysAlive) {
|
||||
}
|
||||
|
||||
EXPECT_TRUE(s.check_for_listener());
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2)); // sorry for making tests slow.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
EXPECT_TRUE(s.check_for_listener());
|
||||
EXPECT_TRUE(l.is_connected());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user