[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:
water111
2021-01-23 16:32:56 -05:00
committed by GitHub
parent 4a97e15b40
commit 27f0a7ca44
95 changed files with 1192 additions and 1120 deletions
+8 -5
View File
@@ -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
+1 -10
View File
@@ -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) {}
};
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+214
View File
@@ -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
+21 -1
View File
@@ -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
+1
View File
@@ -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:
+2
View File
@@ -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
-27
View File
@@ -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
+5 -4
View File
@@ -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;
+54
View File
@@ -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
View File
@@ -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})
+158
View File
@@ -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;
+59
View File
@@ -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 -1
View File
@@ -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"
@@ -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.
-10
View File
@@ -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"
+76 -33
View File
@@ -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 -19
View File
@@ -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"});
}
+141 -16
View File
@@ -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"});
}
-82
View File
@@ -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"});
}
-140
View File
@@ -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"});
}
-68
View File
@@ -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"});
}
-65
View File
@@ -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"});
}
-64
View File
@@ -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"});
}
-63
View File
@@ -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"});
}
-59
View File
@@ -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"});
}
-63
View File
@@ -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"});
}
-88
View File
@@ -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());
-62
View File
@@ -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
}
+49 -1
View File
@@ -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());
+1 -1
View File
@@ -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());
}