Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[decompiler] small fixes for methods and more reference tests #419

Merged
merged 2 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/type_system/TypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ void TypeSystem::add_builtin_types() {
add_field_to_type(basic_type, "type", make_typespec("type"));
// the default new basic doesn't support dynamic sizing. anything dynamic will override this
// and then call (method object new) to do the dynamically-sized allocation.
add_method(basic_type, "new", make_function_typespec({"symbol", "type"}, "basic"));
add_method(basic_type, "new", make_function_typespec({"symbol", "type"}, "_type_"));

// SYMBOL
builtin_structure_inherit(symbol_type);
Expand Down
8 changes: 2 additions & 6 deletions decompiler/IR2/AtomicOpForm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
auto method_id = (ro.offset - 16) / 4;
auto method_info = env.dts->ts.lookup_method(type_name, method_id);

std::vector<DerefToken> tokens;
tokens.push_back(DerefToken::make_field_name("methods-by-name"));
tokens.push_back(DerefToken::make_field_name(method_info.name));
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
nullptr, SimpleAtom::make_var(ro.var).as_expr(), m_my_idx);
return pool.alloc_single_element_form<DerefElement>(nullptr, source, false, tokens);
return pool.alloc_single_element_form<MethodOfTypeElement>(
nullptr, ro.var, input_type.get_type_objects_typespec(), method_info);
}

// todo structure method
Expand Down
27 changes: 27 additions & 0 deletions decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "!=";
case FixedOperatorKind::METHOD_OF_OBJECT:
return "method-of-object";
case FixedOperatorKind::METHOD_OF_TYPE:
return "method-of-type";
case FixedOperatorKind::NULLP:
return "null?";
case FixedOperatorKind::PAIRP:
Expand Down Expand Up @@ -2346,6 +2348,31 @@ void StackSpillValueElement::apply_form(const std::function<void(Form*)>&) {}
void StackSpillValueElement::collect_vars(RegAccessSet&, bool) const {}
void StackSpillValueElement::get_modified_regs(RegSet&) const {}

////////////////////////////////
// MethodOfTypeElement
///////////////////////////////

MethodOfTypeElement::MethodOfTypeElement(RegisterAccess type_reg,
const TypeSpec& type_at_decompile,
const MethodInfo& method_info)
: m_type_reg(type_reg), m_type_at_decompile(type_at_decompile), m_method_info(method_info) {}

goos::Object MethodOfTypeElement::to_form_internal(const Env& env) const {
return pretty_print::build_list("method-of-type", m_type_reg.to_form(env), m_method_info.name);
}

void MethodOfTypeElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}

void MethodOfTypeElement::apply_form(const std::function<void(Form*)>&) {}

void MethodOfTypeElement::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(m_type_reg);
}

void MethodOfTypeElement::get_modified_regs(RegSet&) const {}

////////////////////////////////
// Utilities
////////////////////////////////
Expand Down
28 changes: 28 additions & 0 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,10 @@ class DerefToken {
void apply_form(const std::function<void(Form*)>& f);
void get_modified_regs(RegSet& regs) const;

bool is_field_name(const std::string& name) const {
return m_kind == Kind::FIELD_NAME && m_name == name;
}

Kind kind() const { return m_kind; }
const std::string& field_name() const {
assert(m_kind == Kind::FIELD_NAME);
Expand Down Expand Up @@ -1038,6 +1042,7 @@ class DerefElement : public FormElement {
const Form* base() const { return m_base; }
Form* base() { return m_base; }
const std::vector<DerefToken>& tokens() const { return m_tokens; }
std::vector<DerefToken>& tokens() { return m_tokens; }
void set_base(Form* new_base);
void set_addr_of(bool is_addr_of) { m_is_addr_of = is_addr_of; }

Expand Down Expand Up @@ -1146,6 +1151,7 @@ class ConstantTokenElement : public FormElement {
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
const std::string& value() const { return m_value; }

private:
std::string m_value;
Expand Down Expand Up @@ -1365,6 +1371,28 @@ class StackSpillValueElement : public FormElement {
bool m_is_signed = false;
};

class MethodOfTypeElement : public FormElement {
public:
MethodOfTypeElement(RegisterAccess type_reg,
const TypeSpec& type_at_decompile,
const MethodInfo& method_info);
goos::Object to_form_internal(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(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
void get_modified_regs(RegSet& regs) const override;

private:
RegisterAccess m_type_reg;
TypeSpec m_type_at_decompile;
MethodInfo m_method_info;
};

/*!
* A Form is a wrapper around one or more FormElements.
* This is done for two reasons:
Expand Down
104 changes: 46 additions & 58 deletions decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1677,8 +1677,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
auto desired_arg_type = function_type.get_arg(arg_id + 1);
if (!env.dts->ts.tc(desired_arg_type, actual_arg_type)) {
arg_forms.push_back(
pool.alloc_single_element_form<CastElement>(nullptr, desired_arg_type, val));
arg_forms.push_back(cast_form(val, desired_arg_type, pool, env));
} else {
arg_forms.push_back(val);
}
Expand All @@ -1694,8 +1693,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
auto desired_arg_type = function_type.get_arg(arg_id);
if (!env.dts->ts.tc(desired_arg_type, actual_arg_type)) {
arg_forms.push_back(
pool.alloc_single_element_form<CastElement>(nullptr, desired_arg_type, val));
arg_forms.push_back(cast_form(val, desired_arg_type, pool, env));
} else {
arg_forms.push_back(val);
}
Expand Down Expand Up @@ -1791,13 +1789,13 @@ void FunctionCallElement::update_from_stack(const Env& env,

{
// detect method calls:
// ex: ((-> pair methods-by-name new) (quote global) pair gp-0 a3-0)
// ex: ((method-of-type pair new) (quote global) pair gp-0 a3-0)
constexpr int type_for_method = 0;
constexpr int method_name = 1;

auto deref_matcher = Matcher::deref(
Matcher::any_symbol(type_for_method), false,
{DerefTokenMatcher::string("methods-by-name"), DerefTokenMatcher::any_string(method_name)});
auto deref_matcher = Matcher::op(
GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_TYPE),
{Matcher::any_symbol(type_for_method), Matcher::any_constant_token(method_name)});

auto matcher = Matcher::op_with_rest(GenericOpMatcher::func(deref_matcher), {});
auto temp_form = pool.alloc_single_form(nullptr, new_form);
Expand Down Expand Up @@ -1893,13 +1891,13 @@ void FunctionCallElement::update_from_stack(const Env& env,

{
// detect method calls:
// ex: ((-> XXX methods-by-name new) (quote global) pair gp-0 a3-0)
// ex: ((method-of-type x new) (quote global) pair gp-0 a3-0)
constexpr int method_name = 0;
constexpr int type_source = 1;

auto deref_matcher = Matcher::deref(
Matcher::any(type_source), false,
{DerefTokenMatcher::string("methods-by-name"), DerefTokenMatcher::any_string(method_name)});
auto deref_matcher =
Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_TYPE),
{Matcher::any(type_source), Matcher::any_constant_token(method_name)});

auto matcher = Matcher::op_with_rest(GenericOpMatcher::func(deref_matcher), {});
auto temp_form = pool.alloc_single_form(nullptr, new_form);
Expand Down Expand Up @@ -1942,52 +1940,7 @@ void DerefElement::update_from_stack(const Env& env,
// merge nested ->'s
inline_nested();

if (m_tokens.size() >= 3) {
auto& method_name = m_tokens.at(m_tokens.size() - 1);
auto& mbn = m_tokens.at(m_tokens.size() - 2);
auto& type = m_tokens.at(m_tokens.size() - 3);
if (method_name.kind() == DerefToken::Kind::FIELD_NAME &&
mbn.kind() == DerefToken::Kind::FIELD_NAME && mbn.field_name() == "methods-by-name" &&
type.kind() == DerefToken::Kind::FIELD_NAME && type.field_name() == "type") {
std::string name = method_name.field_name();
m_tokens.pop_back();
m_tokens.pop_back();
m_tokens.pop_back();

if (m_tokens.empty()) {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), m_base,
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, name));
result->push_back(method_op);
} else {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT),
pool.alloc_single_form(nullptr, this),
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, name));
result->push_back(method_op);
}
return;
}
}

// rewrite access to the method table to use method-of-object
// (-> <some-object> type methods-by-name <method-name>)
// (method-of-object <some-object> <method-name>)
auto get_method_matcher = Matcher::deref(
Matcher::any(0), false,
{DerefTokenMatcher::string("type"), DerefTokenMatcher::string("methods-by-name"),
DerefTokenMatcher::any_string(1)});
Form hack_form;
hack_form.elts() = {this};
auto mr = match(get_method_matcher, &hack_form);
if (mr.matched) {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), mr.maps.forms.at(0),
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, mr.maps.strings.at(1)));
result->push_back(method_op);
} else {
result->push_back(this);
}
result->push_back(this);
}

void DerefElement::inline_nested() {
Expand Down Expand Up @@ -3187,6 +3140,41 @@ void VectorFloatLoadStoreElement::push_to_stack(const Env&, FormPool&, FormStack
stack.push_form_element(this, true);
}

void MethodOfTypeElement::update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) {
mark_popped();
auto type = pop_to_forms({m_type_reg}, env, pool, stack, allow_side_effects).at(0);

auto type_as_deref = type->try_as_element<DerefElement>();
if (type_as_deref) {
if (type_as_deref->tokens().size() > 1 &&
type_as_deref->tokens().back().is_field_name("type")) {
type_as_deref->tokens().pop_back();
result->push_back(pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT),
std::vector<Form*>{type, pool.alloc_single_element_form<ConstantTokenElement>(
nullptr, m_method_info.name)}));
return;
} else if (type_as_deref->tokens().size() == 1 &&
type_as_deref->tokens().back().is_field_name("type")) {
result->push_back(pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT),
std::vector<Form*>{
type_as_deref->base(),
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, m_method_info.name)}));
return;
}
}

result->push_back(pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_TYPE),
std::vector<Form*>{type, pool.alloc_single_element_form<ConstantTokenElement>(
nullptr, m_method_info.name)}));
}

void SimpleAtomElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
Expand Down
19 changes: 19 additions & 0 deletions decompiler/IR2/GenericElementMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ Matcher Matcher::while_loop(const Matcher& condition, const Matcher& body) {
return m;
}

Matcher Matcher::any_constant_token(int match_id) {
Matcher m;
m.m_kind = Kind::ANY_CONSTANT_TOKEN;
m.m_string_out_id = match_id;
return m;
}

bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out) const {
switch (m_kind) {
case Kind::ANY:
Expand Down Expand Up @@ -251,6 +258,18 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out) const {
return false;
} break;

case Kind::ANY_CONSTANT_TOKEN: {
auto as_ct = input->try_as_element<ConstantTokenElement>();
if (as_ct) {
if (m_string_out_id != -1) {
maps_out->strings[m_string_out_id] = as_ct->value();
}
return true;
} else {
return false;
}
} break;

case Kind::CAST: {
auto as_cast = dynamic_cast<CastElement*>(input->try_as_single_element());
if (as_cast) {
Expand Down
2 changes: 2 additions & 0 deletions decompiler/IR2/GenericElementMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Matcher {
const Matcher& true_case,
const Matcher& false_case);
static Matcher while_loop(const Matcher& condition, const Matcher& body);
static Matcher any_constant_token(int match_id = -1);

enum class Kind {
ANY_REG, // matching any register
Expand All @@ -64,6 +65,7 @@ class Matcher {
SYMBOL,
IF_WITH_ELSE,
WHILE_LOOP,
ANY_CONSTANT_TOKEN,
INVALID
};

Expand Down
1 change: 1 addition & 0 deletions decompiler/IR2/IR2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum class FixedOperatorKind {
NEQ,
CONS,
METHOD_OF_OBJECT,
METHOD_OF_TYPE,
NULLP,
PAIRP,
NONE,
Expand Down
Loading