[Decompiler] Replace type hint system and improve variable types. (#320)

* get gkernel and gkernel-h at least somewhat working in the offline tests

* strip comments from json

* switch hints to casts. online tests passing, offline passing up to gkernel

* variable retyping is added

* fix up casts in lets

* update
This commit is contained in:
water111
2021-03-13 16:10:39 -05:00
committed by GitHub
parent fe336b7b5f
commit 814480f9e5
61 changed files with 1652 additions and 497 deletions
+63 -18
View File
@@ -7,6 +7,7 @@
#include "decompiler/analysis/final_output.h"
#include "decompiler/analysis/insert_lets.h"
#include "common/goos/PrettyPrinter.h"
#include "common/util/json_util.h"
#include "decompiler/IR2/Form.h"
#include "third-party/json.hpp"
@@ -67,6 +68,43 @@ void FormRegressionTest::TestData::add_string_at_label(const std::string& label_
EXPECT_TRUE(false);
}
std::pair<std::vector<std::string>, std::unordered_map<std::string, LocalVarOverride>>
parse_var_json(const std::string& str) {
if (str.empty()) {
return {};
}
std::vector<std::string> args;
std::unordered_map<std::string, LocalVarOverride> var_overrides;
auto j = parse_commented_json(str);
auto arg = j.find("args");
if (arg != j.end()) {
for (auto& x : arg.value()) {
args.push_back(x);
}
}
auto var = j.find("vars");
if (var != j.end()) {
for (auto& vkv : var->get<std::unordered_map<std::string, nlohmann::json>>()) {
LocalVarOverride override;
if (vkv.second.is_string()) {
override.name = vkv.second.get<std::string>();
} else if (vkv.second.is_array()) {
override.name = vkv.second[0].get<std::string>();
override.type = vkv.second[1].get<std::string>();
} else {
throw std::runtime_error("Invalid function var override.");
}
var_overrides[vkv.first] = override;
}
}
return {args, var_overrides};
}
std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
const std::string& code,
const TypeSpec& function_type,
@@ -74,7 +112,8 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
bool allow_pairs,
const std::string& method_name,
const std::vector<std::pair<std::string, std::string>>& strings,
const std::unordered_map<int, std::vector<TypeHint>>& hints) {
const std::unordered_map<int, std::vector<TypeCast>>& casts,
const std::string& var_map_json) {
dts->type_prop_settings.locked = true;
dts->type_prop_settings.reset();
dts->type_prop_settings.allow_pair = allow_pairs;
@@ -112,7 +151,7 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
test->func.ir2.atomic_ops_succeeded = true;
test->func.ir2.env.set_end_var(test->func.ir2.atomic_ops->end_op().return_var());
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, hints, {}));
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, casts, {}));
test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func));
@@ -133,8 +172,9 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
test->func.ir2.top_form->collect_vars(vars, true);
if (do_expressions) {
auto config = parse_var_json(var_map_json);
bool success = convert_to_expressions(test->func.ir2.top_form, *test->func.ir2.form_pool,
test->func, *dts);
test->func, config.first, config.second, *dts);
EXPECT_TRUE(success);
if (!success) {
@@ -167,9 +207,11 @@ void FormRegressionTest::test(const std::string& code,
bool allow_pairs,
const std::string& method_name,
const std::vector<std::pair<std::string, std::string>>& strings,
const std::unordered_map<int, std::vector<TypeHint>>& hints) {
const std::unordered_map<int, std::vector<TypeCast>>& casts,
const std::string& var_map_json) {
auto ts = dts->parse_type_spec(type);
auto test = make_function(code, ts, do_expressions, allow_pairs, method_name, strings, hints);
auto test = make_function(code, ts, do_expressions, allow_pairs, method_name, strings, casts,
var_map_json);
ASSERT_TRUE(test);
auto expected_form =
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
@@ -193,9 +235,10 @@ void FormRegressionTest::test_final_function(
const std::string& expected,
bool allow_pairs,
const std::vector<std::pair<std::string, std::string>>& strings,
const std::unordered_map<int, std::vector<decompiler::TypeHint>>& hints) {
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts,
const std::string& var_map_json) {
auto ts = dts->parse_type_spec(type);
auto test = make_function(code, ts, true, allow_pairs, "", strings, hints);
auto test = make_function(code, ts, true, allow_pairs, "", strings, casts, var_map_json);
ASSERT_TRUE(test);
auto expected_form =
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
@@ -211,20 +254,22 @@ void FormRegressionTest::test_final_function(
EXPECT_TRUE(expected_form == actual_form);
}
std::unordered_map<int, std::vector<decompiler::TypeHint>> FormRegressionTest::parse_hint_json(
std::unordered_map<int, std::vector<decompiler::TypeCast>> FormRegressionTest::parse_cast_json(
const std::string& in) {
std::unordered_map<int, std::vector<decompiler::TypeHint>> out;
auto hints = nlohmann::json::parse(in);
for (auto& hint : hints) {
auto idx = hint.at(0).get<int>();
for (size_t i = 1; i < hint.size(); i++) {
auto& assignment = hint.at(i);
TypeHint type_hint;
type_hint.reg = Register(assignment.at(0).get<std::string>());
type_hint.type_name = assignment.at(1).get<std::string>();
out[idx].push_back(type_hint);
std::unordered_map<int, std::vector<decompiler::TypeCast>> out;
auto casts = nlohmann::json::parse(in);
for (auto& cast : casts) {
auto idx_range = parse_json_optional_integer_range(cast.at(0));
for (auto idx : idx_range) {
TypeCast type_cast;
type_cast.atomic_op_idx = idx;
type_cast.reg = Register(cast.at(1));
type_cast.type_name = cast.at(2).get<std::string>();
out[idx].push_back(type_cast);
}
}
return out;
}