Skip to content

Commit

Permalink
[decompiler] Handle find-parent-method (#3018)
Browse files Browse the repository at this point in the history
This change adds a few new features:
- Decompiler automatically knows the type of `find-parent-method` use in
jak 1 and jak2 when used in a method or virtual state handler.
- Decompiler inserts a call to `call-parent-method` or
`find-parent-state`
- Removed most casts related to these functions

There are still a few minor issues around this:
- There are still some casts needed when using `post` methods, as `post`
is just a `function`, and needs a cast to `(function none)` or similar.
It didn't seem easy to change the type of `post`, so I'm not going to
worry about it for this PR. It only shows up in like 3 places in jak 2.
(and 0 in jak 1)
- If "call the handler if it's not #f" logic should probably be another
macro.

Fixes #805
  • Loading branch information
water111 authored Sep 30, 2023
1 parent 0f37821 commit 0e31a9c
Show file tree
Hide file tree
Showing 323 changed files with 937 additions and 1,797 deletions.
2 changes: 1 addition & 1 deletion common/formatter/formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ std::string apply_formatting(
}
// TODO - might want to make some kind of per-form config struct, simplify the passing around of
// info below
for (int i = 0; i < curr_node.refs.size(); i++) {
for (int i = 0; i < (int)curr_node.refs.size(); i++) {
const auto& ref = curr_node.refs.at(i);
// Figure out if the element should be inlined or not
bool inline_element = inline_form;
Expand Down
12 changes: 6 additions & 6 deletions common/formatter/rules/formatting_rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void separate_by_newline(std::string& curr_text,
// We only are concerned with top level forms or elements
// Skip the last element, no trailing new-lines (let the editors handle this!)
// Also peek ahead to see if there was a comment on this line, if so don't separate things!
if (!containing_node.metadata.is_top_level || index >= containing_node.refs.size() - 1 ||
if (!containing_node.metadata.is_top_level || index >= (int)containing_node.refs.size() - 1 ||
(containing_node.refs.at(index + 1).metadata.is_comment &&
containing_node.refs.at(index + 1).metadata.is_inline)) {
return;
Expand Down Expand Up @@ -100,7 +100,7 @@ bool form_should_be_constant_paired(const FormatterTreeNode& node) {
return false;
}
int num_pairs = 0;
for (int i = 0; i < node.refs.size() - 1; i++) {
for (int i = 0; i < (int)node.refs.size() - 1; i++) {
const auto& ref = node.refs.at(i);
const auto& next_ref = node.refs.at(i + 1);
if (ref.token && next_ref.token) {
Expand Down Expand Up @@ -147,7 +147,7 @@ int compute_form_width_after_index(const FormatterTreeNode& node,
}
}
int form_width = 0;
for (int i = 0; i < node.refs.size(); i++) {
for (int i = 0; i < (int)node.refs.size(); i++) {
const auto& ref = node.refs.at(i);
if (depth == 0 && i < index) {
continue;
Expand Down Expand Up @@ -275,7 +275,7 @@ void append_newline(std::string& curr_text,
const bool flowing,
const bool constant_pair_form,
const bool force_newline) {
if (force_newline && index >= 1 || (node.metadata.is_comment && !node.metadata.is_inline)) {
if ((force_newline && index >= 1) || (node.metadata.is_comment && !node.metadata.is_inline)) {
curr_text = str_util::rtrim(curr_text) + "\n";
return;
}
Expand Down Expand Up @@ -362,8 +362,8 @@ void align_lines(std::string& text,
alignment_width = 1;
}
std::string aligned_form = "";
for (int i = 0; i < lines.size(); i++) {
if (i >= start_index) {
for (size_t i = 0; i < lines.size(); i++) {
if ((int)i >= start_index) {
aligned_form += str_util::repeat(alignment_width, " ");
}
aligned_form += lines.at(i);
Expand Down
2 changes: 1 addition & 1 deletion common/log/log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ void set_file(const std::string& filename,
existing_log_files = file_util::sort_filepaths(existing_log_files, true);
if (existing_log_files.size() > (LOG_ROTATE_MAX - 1)) {
lg::info("removing {} log files", existing_log_files.size() - (LOG_ROTATE_MAX - 1));
for (int i = 0; i < existing_log_files.size() - (LOG_ROTATE_MAX - 1); i++) {
for (int i = 0; i < (int)existing_log_files.size() - (LOG_ROTATE_MAX - 1); i++) {
lg::info("removing {}", existing_log_files.at(i).string());
fs::remove(existing_log_files.at(i));
}
Expand Down
77 changes: 71 additions & 6 deletions decompiler/IR2/AtomicOpTypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
return TP_Type::make_run_function_in_process_function();
} else if (m_string == "set-to-run" && env.func->name() != "enter-state") {
return TP_Type::make_set_to_run_function();
} else if (m_string == "find-parent-method") {
return TP_Type::make_find_parent_method_function();
}

// look up the type of the symbol
Expand Down Expand Up @@ -1355,10 +1357,6 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
in_type = TypeSpec("function", new_arg_types);
}

if (in_type.arg_count() < 1) {
throw std::runtime_error("Called a function, but we do not know its type");
}

if (in_type.arg_count() == 2 && in_type.get_arg(0) == TypeSpec("_varargs_")) {
// we're calling a varags function, which is format. We can determine the argument count
// by looking at the format string, if we can get it.
Expand Down Expand Up @@ -1418,12 +1416,79 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
arg_type.print());
}
}

bool use_normal_last_arg = true;

if (in_tp.kind == TP_Type::Kind::FIND_PARENT_METHOD_FUNCTION) {
bool can_use_call_parent = true;
TypeSpec call_parent_result_type;
const auto& guessed_name = env.func->guessed_name;

if (guessed_name.kind == FunctionName::FunctionKind::METHOD ||
guessed_name.kind == FunctionName::FunctionKind::V_STATE) {
// should call something like:
// (find-parent-method sharkey 39)
const auto& type_arg = input.get(Register(Reg::GPR, Reg::A0));
if (can_use_call_parent && type_arg.kind != TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
lg::warn(
"Can't use call-parent-method because the first argument to find-parent-method is a "
"{}, which should be a type",
type_arg.print());
can_use_call_parent = false;
}

if (can_use_call_parent && type_arg.get_type_objects_typespec() != guessed_name.type_name) {
lg::warn(
"Can't use call-parent-method because the first argument type is wrong: got {}, but "
"expected {}",
type_arg.get_type_objects_typespec().print(), guessed_name.type_name);
can_use_call_parent = false;
}
const auto& id_arg = input.get(Register(Reg::GPR, Reg::A1));
int expected_id = -1;
if (guessed_name.kind == FunctionName::FunctionKind::V_STATE) {
auto state_info = dts.ts.lookup_method(guessed_name.type_name, guessed_name.state_name);
expected_id = state_info.id;
call_parent_result_type =
state_info.type.substitute_for_method_call(guessed_name.type_name);
} else {
expected_id = guessed_name.method_id;
call_parent_result_type = env.func->type; // same type as this method!
}
if (can_use_call_parent && !id_arg.is_integer_constant(expected_id)) {
lg::warn(
"Can't use call-parent-method because the second argument is wrong: got {}, but "
"expected a constant integer {}",
id_arg.print(), expected_id);
can_use_call_parent = false;
}

} else {
can_use_call_parent = false;
lg::warn(
"Can't use call-parent-method because find-parent-method was called in {}, which isn't a "
"method or state handler.",
env.func->name());
}

if (can_use_call_parent) {
end_types.get(Register(Reg::GPR, Reg::V0)) = TP_Type::make_from_ts(call_parent_result_type);
use_normal_last_arg = false;
}
}

if (in_type.arg_count() < 1) {
throw std::runtime_error("Called a function, but we do not know its type");
}

if (use_normal_last_arg) {
end_types.get(Register(Reg::GPR, Reg::V0)) = TP_Type::make_from_ts(in_type.last_arg());
}

// set the call type!
m_call_type = in_type;
m_call_type_set = true;

end_types.get(Register(Reg::GPR, Reg::V0)) = TP_Type::make_from_ts(in_type.last_arg());

if (in_tp.kind == TP_Type::Kind::NON_OBJECT_NEW_METHOD &&
in_type.last_arg() == TypeSpec("array")) {
// array new:
Expand Down
37 changes: 36 additions & 1 deletion decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2621,7 +2621,9 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
}
}

FormElement* SetFormFormElement::make_set_time(const Env& env, FormPool& pool, FormStack& stack) {
FormElement* SetFormFormElement::make_set_time(const Env& /*env*/,
FormPool& pool,
FormStack& /*stack*/) {
auto matcher = match(
Matcher::op(GenericOpMatcher::func(Matcher::constant_token("current-time")), {}), m_src);
if (matcher.matched) {
Expand Down Expand Up @@ -3908,6 +3910,39 @@ void FunctionCallElement::update_from_stack(const Env& env,
}
}

// detect call-parent-method
{
const auto& guessed_name = env.func->guessed_name;
if (guessed_name.kind == FunctionName::FunctionKind::METHOD) {
// detect stuff like: ((find-parent-method...) arg...)
auto mr_find_parent =
match(Matcher::func(Matcher::symbol("find-parent-method"),
{Matcher::symbol(env.func->method_of_type),
Matcher::integer(env.func->guessed_name.method_id)}),
unstacked.at(0)

);
if (mr_find_parent.matched) {
new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("call-parent-method")),
arg_forms);
}
} else if (guessed_name.kind == FunctionName::FunctionKind::V_STATE && arg_forms.size() == 2) {
// here, simply detect (find-parent-method...)
//
auto mr_find_parent = match(Matcher::symbol("find-parent-method"), unstacked.at(0));
if (mr_find_parent.matched) {
auto state_info =
env.dts->ts.lookup_method(guessed_name.type_name, guessed_name.state_name);
if (arg_forms.at(0)->to_string(env) == guessed_name.type_name &&
arg_forms.at(1)->to_string(env) == std::to_string(state_info.id)) {
new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("find-parent-state")));
}
}
}
}

result->push_back(new_form);
}

Expand Down
8 changes: 4 additions & 4 deletions decompiler/IR2/GenericElementMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* cons
} else if (m_kind == Kind::VAR_NAME) {
return env && env->get_variable_name_name_only(result) == m_str;
} else if (m_reg_out_id != -1) {
if (m_kind == Kind::SAME_VAR && maps_out->regs.size() > m_reg_out_id &&
if (m_kind == Kind::SAME_VAR && (int)maps_out->regs.size() > m_reg_out_id &&
maps_out->regs.at(m_reg_out_id)) {
return env && env->get_variable_name_name_only(result) ==
env->get_variable_name_name_only(*maps_out->regs.at(m_reg_out_id));
Expand Down Expand Up @@ -695,7 +695,7 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* cons
if (as_let) {
// fail if we have wrong number of entries/body elts or recursive marker
if ((m_entry_matchers.size() != as_let->entries().size()) ||
(m_sub_matchers.size() != as_let->body()->size()) ||
((int)m_sub_matchers.size() != as_let->body()->size()) ||
(as_let->is_star() != m_let_is_star)) {
return false;
}
Expand All @@ -706,7 +706,7 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* cons
}
}
// now match body
for (int i = 0; i < m_sub_matchers.size(); ++i) {
for (int i = 0; i < (int)m_sub_matchers.size(); ++i) {
Form fake;
fake.elts().push_back(as_let->body()->elts().at(i));
if (!m_sub_matchers.at(i).do_match(&fake, maps_out, env)) {
Expand Down Expand Up @@ -908,7 +908,7 @@ bool LetEntryMatcher::do_match(const LetElement::Entry& input,
case Kind::ANY:
case Kind::NAME:
if (m_reg_out_id != -1) {
if (m_kind == Kind::NAME && maps_out->regs.size() > m_reg_out_id &&
if (m_kind == Kind::NAME && (int)maps_out->regs.size() > m_reg_out_id &&
maps_out->regs.at(m_reg_out_id)) {
return env && env->get_variable_name_name_only(input.dest) ==
env->get_variable_name_name_only(*maps_out->regs.at(m_reg_out_id));
Expand Down
Loading

0 comments on commit 0e31a9c

Please sign in to comment.