Clean up some errors/crashes in decompiler (#452)

* fix offline tests, clean up some warnings

* clean up warnings during decomp

* fix remaining crash issues
This commit is contained in:
water111 2021-05-11 16:43:13 -04:00 committed by GitHub
parent 1d72e79df8
commit 433993074a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 194 additions and 113 deletions

View file

@ -10,6 +10,21 @@
#include <third-party/fmt/core.h>
#include "TypeSystem.h"
#include "common/util/math_util.h"
#include "third-party/fmt/color.h"
namespace {
template <typename... Args>
[[noreturn]] void throw_typesystem_error(const std::string& str, Args&&... args) {
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Type Error! --\n");
if (!str.empty() && str.back() == '\n') {
fmt::print(fg(fmt::color::yellow), str, std::forward<Args>(args)...);
} else {
fmt::print(fg(fmt::color::yellow), str + '\n', std::forward<Args>(args)...);
}
throw std::runtime_error("Type Error");
}
} // namespace
TypeSystem::TypeSystem() {
// the "none" and "_type_" types are included by default.
@ -29,10 +44,10 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
if (*kv->second != *type) {
// exists, and we are trying to change it!
fmt::print("[TypeSystem] Type {} was originally\n{}\nand is redefined as\n{}\n",
kv->second->get_name(), kv->second->print(), type->print());
if (m_allow_redefinition) {
fmt::print("[TypeSystem] Type {} was originally\n{}\nand is redefined as\n{}\n",
kv->second->get_name(), kv->second->print(), type->print());
// extra dangerous, we have allowed type redefinition!
// keep the unique_ptr around, just in case somebody references this old type pointer.
@ -41,7 +56,9 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
// update the type
m_types[name] = std::move(type);
} else {
throw std::runtime_error("Type was redefined with throw_on_redefine set.");
throw_typesystem_error(
"Inconsistent type definition. Type {} was originally\n{}\nand is redefined as\n{}\n",
kv->second->get_name(), kv->second->print(), type->print());
}
}
} else {
@ -50,15 +67,14 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
// none/object get to skip these checks because they are roots.
if (name != "object" && name != "none" && name != "_type_" && name != "_varargs_") {
if (m_forward_declared_types.find(type->get_parent()) != m_forward_declared_types.end()) {
fmt::print("[TypeSystem] Type {} has incompletely defined parent {}\n", type->get_name(),
type->get_parent());
throw std::runtime_error("add_type failed");
throw_typesystem_error(
"Cannot create new type {}. The parent type {} is not fully defined.\n",
type->get_name(), type->get_parent());
}
if (m_types.find(type->get_parent()) == m_types.end()) {
fmt::print("[TypeSystem] Type {} has undefined parent {}\n", type->get_name(),
type->get_parent());
throw std::runtime_error("add_type failed");
throw_typesystem_error("Cannot create new type {}. The parent type {} is not defined.\n",
type->get_name(), type->get_parent());
}
}
@ -185,8 +201,7 @@ TypeSpec TypeSystem::make_typespec(const std::string& name) const {
m_forward_declared_types.find(name) != m_forward_declared_types.end()) {
return TypeSpec(name);
} else {
fmt::print("[TypeSystem] The type {} is unknown.\n", name);
throw std::runtime_error("make_typespec failed");
throw_typesystem_error("Type {} is unknown\n", name);
}
}
@ -194,6 +209,10 @@ bool TypeSystem::fully_defined_type_exists(const std::string& name) const {
return m_types.find(name) != m_types.end();
}
bool TypeSystem::fully_defined_type_exists(const TypeSpec& type) const {
return fully_defined_type_exists(type.base_type());
}
bool TypeSystem::partially_defined_type_exists(const std::string& name) const {
return m_forward_declared_types.find(name) != m_forward_declared_types.end();
}
@ -257,9 +276,9 @@ Type* TypeSystem::lookup_type(const std::string& name) const {
}
if (m_forward_declared_types.find(name) != m_forward_declared_types.end()) {
fmt::print("[TypeSystem] The type {} is not fully defined.\n", name);
throw_typesystem_error("Type {} is not fully defined.\n", name);
} else {
fmt::print("[TypeSystem] The type {} is not defined.\n", name);
throw_typesystem_error("Type {} is not defined.\n", name);
}
throw std::runtime_error("lookup_type failed");
}
@ -300,17 +319,13 @@ Type* TypeSystem::lookup_type_allow_partial_def(const std::string& name) const {
} else if (fwd_dec->second == BASIC) {
return lookup_type("basic");
} else {
fmt::print(
"[TypeSystem] The type {} is known to be a type, but has not been defined with deftype "
throw_typesystem_error(
"The type {} is known to be a type, but has not been defined with deftype "
"or properly forward declared with declare-type\n",
name);
}
} else {
fmt::print("[TypeSystem] The type {} is not defined.\n", name);
}
throw std::runtime_error("lookup_type_allow_partial_def failed");
throw_typesystem_error("The type {} is unknown.\n", name);
}
MethodInfo TypeSystem::add_method(const std::string& type_name,
@ -359,21 +374,17 @@ MethodInfo TypeSystem::add_method(Type* type,
if (got_existing) {
// make sure we aren't changing anything.
if (!existing_info.type.is_compatible_child_method(ts, type->get_name())) {
fmt::print(
"[TypeSystem] The method {} of type {} was originally defined as {}, but has been "
throw_typesystem_error(
"The method {} of type {} was originally defined as {}, but has been "
"redefined as {}\n",
method_name, type->get_name(), existing_info.type.print(), ts.print());
// unlike type re-definition, method re-definition is almost certain to go wrong.
// probably better to give up.
throw std::runtime_error("method redefinition");
}
return existing_info;
} else {
if (!allow_new_method) {
fmt::print("[TypeSystem] Attempted to add method {} to type {} but it was not declared.\n",
method_name, type->get_name());
throw std::runtime_error("illegal method definition");
throw_typesystem_error("Cannot add method {} to type {} because it was not declared.\n",
method_name, type->get_name());
}
// add a new method!
return type->add_method({get_next_method_id(type), method_name, ts, type->get_name()});
@ -390,11 +401,10 @@ MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) {
if (type->get_my_new_method(&existing)) {
// it exists!
if (!existing.type.is_compatible_child_method(ts, type->get_name())) {
fmt::print(
"[TypeSystem] The new method of {} was originally defined as {}, but has been redefined "
"as {}\n",
throw_typesystem_error(
"Cannot add new method. Type does not match declaration. The new method of {} was "
"originally defined as {}, but has been redefined as {}\n",
type->get_name(), existing.type.print(), ts.print());
throw std::runtime_error("add_new_method failed");
}
return existing;
@ -433,8 +443,7 @@ MethodInfo TypeSystem::lookup_method(const std::string& type_name,
}
}
fmt::print("[TypeSystem] The method {} of type {} could not be found.\n", method_name, type_name);
throw std::runtime_error("lookup_method failed");
throw_typesystem_error("The method {} of type {} could not be found.\n", method_name, type_name);
}
bool TypeSystem::try_lookup_method(const std::string& type_name,
@ -531,9 +540,8 @@ MethodInfo TypeSystem::lookup_method(const std::string& type_name, int method_id
}
}
fmt::print("[TypeSystem] The method with id {} of type {} could not be found.\n", method_id,
type_name);
throw std::runtime_error("lookup_method failed");
throw_typesystem_error("The method with id {} of type {} could not be found.\n", method_id,
type_name);
}
/*!
@ -560,8 +568,7 @@ MethodInfo TypeSystem::lookup_new_method(const std::string& type_name) const {
}
}
fmt::print("[TypeSystem] The new method of type {} could not be found.\n", type_name);
throw std::runtime_error("lookup_new_method failed");
throw_typesystem_error("The new method of type {} could not be found.\n", type_name);
}
/*!
@ -572,9 +579,9 @@ void TypeSystem::assert_method_id(const std::string& type_name,
int id) {
auto info = lookup_method(type_name, method_name);
if (info.id != id) {
fmt::print(
"[TypeSystem] Method ID assertion failed: type {}, method {} id was {}, expected {}\n",
type_name, method_name, info.id, id);
throw_typesystem_error(
"Method ID assertion failed: type {}, method {} id was {}, expected {}\n", type_name,
method_name, info.id, id);
}
}
@ -634,8 +641,8 @@ void TypeSystem::assert_field_offset(const std::string& type_name,
int offset) {
Field field = lookup_field(type_name, field_name);
if (field.offset() != offset) {
fmt::print("[TypeSystem] assert_field_offset({}, {}, {}) failed - got {}\n", type_name,
field_name, offset);
throw_typesystem_error("assert_field_offset({}, {}, {}) failed - got {}\n", type_name,
field_name, offset);
throw std::runtime_error("assert_field_offset failed");
}
}
@ -652,8 +659,7 @@ int TypeSystem::add_field_to_type(StructureType* type,
int offset_override,
bool skip_in_static_decomp) {
if (type->lookup_field(field_name, nullptr)) {
fmt::print("[TypeSystem] Type {} already has a field named {}\n", type->get_name(), field_name);
throw std::runtime_error("add_field_to_type duplicate field names");
throw_typesystem_error("Type {} already has a field named {}\n", type->get_name(), field_name);
}
// first, construct the field
@ -681,11 +687,8 @@ int TypeSystem::add_field_to_type(StructureType* type,
int aligned_offset = align(offset, field_alignment);
field.mark_as_user_placed();
if (offset != aligned_offset) {
fmt::print(
"[TypeSystem] Tried to overwrite offset of field to be {}, but it is not aligned "
"correctly\n",
offset);
throw std::runtime_error("add_field_to_type bad offset_override");
throw_typesystem_error("Tried to place field {} at {}, but it is not aligned correctly\n",
field_name, offset);
}
}
@ -909,8 +912,7 @@ Field TypeSystem::lookup_field(const std::string& type_name, const std::string&
auto type = get_type_of_type<StructureType>(type_name);
Field field;
if (!type->lookup_field(field_name, &field)) {
fmt::print("[TypeSystem] Type {} has no field named {}\n", type_name, field_name);
throw std::runtime_error("lookup_field failed");
throw_typesystem_error("Type {} has no field named {}\n", type_name, field_name);
}
return field;
}
@ -961,11 +963,14 @@ int TypeSystem::get_size_in_type(const Field& field) const {
if (field.is_array()) {
if (field.is_inline()) {
if (!fully_defined_type_exists(field.type())) {
throw_typesystem_error("Cannot use the forward-declared type {} in an inline array.\n",
field.type().print());
}
if (!allow_inline(field_type)) {
fmt::print(
"[Type System] Attempted to use `{}` inline, this probably isn't what you wanted.\n",
throw_typesystem_error(
"Attempted to use `{}` inline, this probably isn't what you wanted.\n",
field_type->get_name());
throw std::runtime_error("bad get size in type");
}
assert(field_type->is_reference());
return field.array_size() * align(field_type->get_size_in_memory(),
@ -981,12 +986,15 @@ int TypeSystem::get_size_in_type(const Field& field) const {
} else {
// not an array
if (field.is_inline()) {
if (!fully_defined_type_exists(field.type())) {
throw_typesystem_error("Cannot use the forward-declared type {} inline.\n",
field.type().print());
}
if (!allow_inline(field_type)) {
fmt::print(
"[Type System] Attempted to use `{}` inline, this probably isn't what you wanted. Type "
throw_typesystem_error(
"Attempted to use `{}` inline, this probably isn't what you wanted. Type "
"may not be defined fully.\n",
field_type->get_name());
throw std::runtime_error("bad get size in type");
}
assert(field_type->is_reference());
// return align(field_type->get_size_in_memory(), field_type->get_in_memory_alignment());
@ -1275,8 +1283,7 @@ BitfieldLookupInfo TypeSystem::lookup_bitfield_info(const std::string& type_name
auto type = get_type_of_type<BitFieldType>(type_name);
BitField f;
if (!type->lookup_field(field_name, &f)) {
fmt::print("[TypeSystem] Type {} has no bitfield named {}\n", type_name, field_name);
throw std::runtime_error("lookup_bitfield failed");
throw_typesystem_error("Type {} has no bitfield named {}\n", type_name, field_name);
}
BitfieldLookupInfo result;
@ -1303,19 +1310,17 @@ void TypeSystem::add_field_to_bitfield(BitFieldType* type,
}
if (field_size > load_size) {
fmt::print(
"[TypeSystem] Type {}'s bitfield {}'s set size is {}, which is larger than the actual "
throw_typesystem_error(
"Type {}'s bitfield {}'s set size is {}, which is larger than the actual "
"type: {}\n",
type->get_name(), field_name, field_size, load_size);
throw std::runtime_error("Failed to add bitfield to type");
}
if (field_size + offset > type->get_load_size() * 8) {
fmt::print(
"[TypeSystem] Type {}'s bitfield {} will run off the end of the type (ends at {} bits, "
throw_typesystem_error(
"Type {}'s bitfield {} will run off the end of the type (ends at {} bits, "
"type is {} bits)\n",
type->get_name(), field_name, field_size + offset, type->get_load_size() * 8);
throw std::runtime_error("Failed to add bitfield to type");
}
BitField field(field_type, field_name, offset, field_size);
type->m_fields.push_back(field);

View file

@ -100,6 +100,7 @@ class TypeSystem {
FieldReverseLookupOutput reverse_field_lookup(const FieldReverseLookupInput& input) const;
bool fully_defined_type_exists(const std::string& name) const;
bool fully_defined_type_exists(const TypeSpec& type) const;
bool partially_defined_type_exists(const std::string& name) const;
TypeSpec make_typespec(const std::string& name) const;
TypeSpec make_array_typespec(const TypeSpec& element_type) const;

View file

@ -1386,8 +1386,6 @@ bool ControlFlowGraph::find_cond_w_empty_else() {
new_cwe->prev->next = new_cwe;
}
lg::error("There is a very strange control flow here, please check it manually.");
// link new_cwe <-> end
std::vector<CfgVtx*> to_replace;
// to_replace.push_back(else_block);

View file

@ -483,9 +483,9 @@ const UseDefInfo& Env::get_use_def_info(const RegisterAccess& ra) const {
return m_var_names.use_def_info.at(var_id);
}
void Env::disable_def(const RegisterAccess& access) {
void Env::disable_def(const RegisterAccess& access, DecompWarnings& warnings) {
if (has_local_vars()) {
m_var_names.disable_def(access);
m_var_names.disable_def(access, warnings);
}
}

View file

@ -168,7 +168,7 @@ class Env {
}
}
void disable_def(const RegisterAccess& access);
void disable_def(const RegisterAccess& access, DecompWarnings& warnings);
void set_defined_in_let(const std::string& var) { m_vars_defined_in_let.insert(var); }
@ -180,6 +180,8 @@ class Env {
// todo - remove these hacks at some point.
LinkedObjectFile* file = nullptr;
DecompilerTypeSystem* dts = nullptr;
Function* func = nullptr;
std::unordered_map<int, StackSpillEntry> stack_slot_entries;
std::string get_spill_slot_var_name(int offset) const {

View file

@ -52,6 +52,10 @@
namespace decompiler {
namespace {
bool debug_method_calls = false;
}
bool Form::has_side_effects() {
bool has_side_effect = false;
apply([&](FormElement* elt) {
@ -1740,16 +1744,20 @@ void FunctionCallElement::update_from_stack(const Env& env,
if (unsafe->try_as_single_element()->to_form(env) ==
mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) {
resolved = true;
lg::warn(
fmt::format("Rare method call (type 1). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
if (debug_method_calls) {
lg::warn(fmt::format(
"Rare method call (type 1). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
}
}
}
if (!resolved) {
lg::warn(
fmt::format("Rare method call (type 2). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
if (debug_method_calls) {
lg::warn(
fmt::format("Rare method call (type 2). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
}
auto unsafe_as_se = dynamic_cast<SimpleExpressionElement*>(unsafe->try_as_single_element());
if (unsafe_as_se && unsafe_as_se->expr().is_identity() &&
@ -1763,7 +1771,9 @@ void FunctionCallElement::update_from_stack(const Env& env,
} else {
if (unsafe_2->try_as_single_element()->to_form(env) ==
mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) {
lg::warn("Check even more carefully");
if (debug_method_calls) {
lg::warn("Check even more carefully");
}
resolved = true;
unsafe = unsafe_2;
}
@ -1879,12 +1889,8 @@ void FunctionCallElement::update_from_stack(const Env& env,
result->push_back(new_op);
return;
}
} else {
lg::warn("Got a suspicious new method. This may be fine, but should be uncommon: {}",
temp_form->to_string(env));
// throw std::runtime_error("Failed to match new method: " +
// temp_form->to_string(env));
}
// possible else case here to catch fixed-type new's in a nicer way
}
}
}
@ -2165,7 +2171,7 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
for (size_t i = 0; i < dest_sets.size() - 1; i++) {
auto var = dest_sets.at(i)->dst();
auto* env2 = const_cast<Env*>(&env);
env2->disable_def(var);
env2->disable_def(var, env2->func->warnings);
}
}

View file

@ -5,6 +5,7 @@
#include "decompiler/Disasm/Register.h"
#include "decompiler/util/TP_Type.h"
#include "third-party/fmt/core.h"
#include "decompiler/Function/Warnings.h"
namespace decompiler {
enum class AccessMode : u8 {
@ -172,13 +173,13 @@ struct UseDefInfo {
throw std::runtime_error("Invalid disable use");
}
void disable_def(int op_id) {
void disable_def(int op_id, DecompWarnings& warnings) {
for (auto& x : defs) {
if (x.op_id == op_id) {
if (x.disabled) {
lg::warn(
warnings.general_warning(
"disable def twice: {}. This may happen when a cond (no else) is nested inside of "
"another conditional, but it should be rare.\n",
"another conditional, but it should be rare.",
x.op_id);
}
x.disabled = true;
@ -240,10 +241,10 @@ struct VariableNames {
use_def_info.at(RegId(access.reg(), var_id)).disable_use(access.idx());
}
void disable_def(const RegisterAccess& access) {
void disable_def(const RegisterAccess& access, DecompWarnings& warnings) {
assert(access.mode() == AccessMode::WRITE);
auto var_id = write_opid_to_varid.at(access.reg()).at(access.idx());
use_def_info.at(RegId(access.reg(), var_id)).disable_def(access.idx());
use_def_info.at(RegId(access.reg(), var_id)).disable_def(access.idx(), warnings);
}
const VarInfo& lookup(Register reg, int op_id, AccessMode mode) const {

View file

@ -183,6 +183,7 @@ void ObjectFileDB::ir2_basic_block_pass() {
total_functions++;
func.ir2.env.file = &data.linked_data;
func.ir2.env.dts = &dts;
func.ir2.env.func = &func;
// first, find basic blocks.
auto blocks = find_blocks_in_function(data.linked_data, segment_id, func);
@ -206,14 +207,12 @@ void ObjectFileDB::ir2_basic_block_pass() {
// run analysis
// build a control flow graph, just looking at branch instructions.
// if (func.guessed_name.to_string() == "abs") {
func.cfg = build_cfg(data.linked_data, segment_id, func);
if (!func.cfg->is_fully_resolved()) {
lg::warn("Function {} from {} failed to build control flow graph!",
func.guessed_name.to_string(), data.to_unique_name());
failed_to_build_cfg++;
}
// }
// if we got an inspect method, inspect it.
if (func.is_inspect_method) {
@ -317,7 +316,8 @@ void ObjectFileDB::ir2_type_analysis_pass() {
if (!func.suspected_asm) {
non_asm_functions++;
TypeSpec ts;
if (lookup_function_type(func.guessed_name, data.to_unique_name(), &ts)) {
if (lookup_function_type(func.guessed_name, data.to_unique_name(), &ts) &&
func.ir2.atomic_ops_succeeded) {
func.type = ts;
attempted_functions++;
// try type analysis here.

View file

@ -341,11 +341,11 @@ bool try_clean_up_sc_as_and(FormPool& pool, Function& func, ShortCircuitElement*
} else {
// lg::warn("Disabling def of {} in final or delay slot",
// as_set->to_string(func.ir2.env));
func.ir2.env.disable_def(as_set->dst());
func.ir2.env.disable_def(as_set->dst(), func.warnings);
}
} else {
// lg::warn("Disabling def of {} in or delay slot", as_set->to_string(func.ir2.env));
func.ir2.env.disable_def(as_set->dst());
func.ir2.env.disable_def(as_set->dst(), func.warnings);
}
}
@ -464,12 +464,12 @@ bool try_clean_up_sc_as_or(FormPool& pool, Function& func, ShortCircuitElement*
} else {
// lg::warn("Disabling def of {} in final or delay slot",
// as_set->to_string(func.ir2.env));
func.ir2.env.disable_def(as_set->dst());
func.ir2.env.disable_def(as_set->dst(), func.warnings);
}
} else {
// lg::warn("Disabling def of {} in or delay slot", as_set->to_string(func.ir2.env));
func.ir2.env.disable_def(as_set->dst());
func.ir2.env.disable_def(as_set->dst(), func.warnings);
}
}
@ -612,7 +612,7 @@ void convert_cond_no_else_to_compare(FormPool& pool,
assert(cne->entries.size() == 1);
// safe to do this here because we never give up on this.
f.ir2.env.disable_def(condition.first->op()->branch_delay().var(0));
f.ir2.env.disable_def(condition.first->op()->branch_delay().var(0), f.warnings);
auto condition_as_single =
dynamic_cast<BranchElement*>(cne->entries.front().condition->try_as_single_element());
@ -692,7 +692,7 @@ void clean_up_cond_no_else_final(Function& func, CondNoElseElement* cne) {
if (func.ir2.env.has_reg_use()) {
auto reg = cne->entries.at(i).false_destination;
// lg::warn("Disable def of {} at {}\n", reg->to_string(func.ir2.env), reg->idx());
func.ir2.env.disable_def(*reg);
func.ir2.env.disable_def(*reg, func.warnings);
}
}
}
@ -1132,7 +1132,7 @@ Form* try_sc_as_ash(FormPool& pool, Function& f, const ShortCircuit* vtx) {
f.ir2.env.disable_use(dsubu_var);
// and the def too
f.ir2.env.disable_def(dsrav_set->dst());
f.ir2.env.disable_def(dsrav_set->dst(), f.warnings);
return b0_c_ptr;
}
@ -1283,8 +1283,8 @@ Form* try_sc_as_type_of(FormPool& pool, Function& f, const ShortCircuit* vtx) {
b0_ptr->push_back(op);
// fix register info
f.ir2.env.disable_def(b0_delay_op.dst());
f.ir2.env.disable_def(b1_delay_op.dst());
f.ir2.env.disable_def(b0_delay_op.dst(), f.warnings);
f.ir2.env.disable_def(b1_delay_op.dst(), f.warnings);
f.ir2.env.disable_use(shift->expr().get_arg(0).var());
return b0_ptr;

View file

@ -89,7 +89,7 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_
}
} catch (std::exception& e) {
auto info = m_reader.db.get_info_for(o);
lg::error("Error {} when parsing decompiler type file:\n{}", e.what(), info);
lg::error("{} when parsing decompiler type file:\n{}", e.what(), info);
throw e;
}
});

View file

@ -324,8 +324,6 @@ goos::Object decompile_structure(const TypeSpec& type,
// we can specify a more specific type.
auto got_type = TypeSpec(word.symbol_name);
if (ts.tc(actual_type, got_type)) {
lg::info("For type {}, got more specific type {}\n", actual_type.print(),
got_type.print());
actual_type = got_type;
if (actual_type == TypeSpec("string")) {
return decompile_string_at_label(label, words);
@ -601,7 +599,7 @@ goos::Object decompile_value(const TypeSpec& type,
assert(bytes.size() == 4);
s32 value;
memcpy(&value, bytes.data(), 4);
if (value > 100 && value <= INT32_MAX) {
if (value > 100) {
return pretty_print::to_symbol(fmt::format("#x{:x}", value));
} else {
return pretty_print::to_symbol(fmt::format("{}", value));
@ -615,7 +613,7 @@ goos::Object decompile_value(const TypeSpec& type,
assert(bytes.size() == 2);
s16 value;
memcpy(&value, bytes.data(), 2);
if (value > 100 && value <= INT16_MAX) {
if (value > 100) {
return pretty_print::to_symbol(fmt::format("#x{:x}", value));
} else {
return pretty_print::to_symbol(fmt::format("{}", value));

View file

@ -329,13 +329,22 @@ bool Compiler::connect_to_target() {
/*!
* Just run the front end on a string. Will not do register allocation or code generation.
* Useful for typechecking or running strings that invoke the compiler again.
* Useful for typechecking, defining types, or running strings that invoke the compiler again.
*/
void Compiler::run_front_end_on_string(const std::string& src) {
auto code = m_goos.reader.read_from_string({src});
compile_object_file("run-on-string", code, true);
}
/*!
* Just run the front end on a file. Will not do register allocation or code generation.
* Useful for typechecking, defining types, or running strings that invoke the compiler again.
*/
void Compiler::run_front_end_on_file(const std::vector<std::string>& path) {
auto code = m_goos.reader.read_from_file(path);
compile_object_file("run-on-file", code, true);
}
/*!
* Run the entire compilation process on the input source code. Will generate an object file, but
* won't save it anywhere.

View file

@ -38,6 +38,7 @@ class Compiler {
std::vector<std::string> run_test_no_load(const std::string& source_code);
void compile_and_send_from_string(const std::string& source_code);
void run_front_end_on_string(const std::string& src);
void run_front_end_on_file(const std::vector<std::string>& path);
void run_full_compiler_on_string_no_save(const std::string& src);
void shutdown_target();
void enable_throw_on_redefines() { m_throw_on_define_extern_redefinition = true; }

View file

@ -133,6 +133,7 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
// Set up the environment
test->func.ir2.env.file = &test->file;
test->func.ir2.env.dts = dts.get();
test->func.ir2.env.func = &test->func;
// Set up the function
test->func.instructions = program.instructions;
test->func.guessed_name.set_as_global("test-function");

View file

@ -472,6 +472,19 @@
)
;; game-h - TODO
(deftype vector (structure)
((data float 4 :offset-assert 0)
(x float :offset 0)
(y float :offset 4)
(z float :offset 8)
(w float :offset 12)
(quad uint128 :offset 0)
)
:method-count-assert 9
:size-assert #x10
:flag-assert #x900000010
)
(deftype attack-info (structure)
((trans vector :inline :offset-assert 0)
(vector vector :inline :offset-assert 16)
@ -496,4 +509,47 @@
(:methods
(dummy-9 () none 9)
)
)
)
;; definition of type quaternion
(deftype quaternion (structure)
((x float :offset-assert 0)
(y float :offset-assert 4)
(z float :offset-assert 8)
(w float :offset-assert 12)
(data float 4 :offset 0)
(vec vector :inline :offset 0)
(quad uint128 :offset 0)
)
:method-count-assert 9
:size-assert #x10
:flag-assert #x900000010
)
(deftype transform (structure)
((trans vector :inline :offset-assert 0)
(rot vector :inline :offset-assert 16)
(scale vector :inline :offset-assert 32)
)
:method-count-assert 9
:size-assert #x30
:flag-assert #x900000030
)
;; transformq
(deftype transformq (transform)
;; this overlays the rot field of transform.
((quat quaternion :inline :offset 16)
)
:method-count-assert 9
:size-assert #x30
:flag-assert #x900000030
)
(declare-type target basic)
(define-extern *target* target)
(declare-type sidekick basic)
(define-extern *sidekick* basic)

View file

@ -149,6 +149,7 @@
)
;; definition for function file-info-correct-version?
;; WARN: disable def twice: 16. This may happen when a cond (no else) is nested inside of another conditional, but it should be rare.
(defun
file-info-correct-version?
((info file-info) (kind file-kind) (version-override int))

View file

@ -28,9 +28,10 @@
(no-load-wait uint64 :offset-assert 568)
(no-look-around-wait uint64 :offset-assert 576)
)
:heap-base #x1e0
:method-count-assert 21
:size-assert #x248
:flag-assert #x1500000248
:flag-assert #x1501e00248
(:methods
(dummy-20 () none 20)
)
@ -79,9 +80,10 @@
(anim-seed uint64 :offset 192)
(shadow-in-movie? basic :offset-assert 200)
)
:heap-base #x60
:method-count-assert 20
:size-assert #xcc
:flag-assert #x14000000cc
:flag-assert #x14006000cc
)
;; definition for method 3 of type sidekick

View file

@ -489,8 +489,8 @@ int line_count(const std::string& str) {
TEST_F(OfflineDecompilation, Compile) {
Compiler compiler;
compiler.run_front_end_on_string(file_util::read_text_file(file_util::get_file_path(
{"test", "decompiler", "reference", "all_forward_declarations.gc"})));
compiler.run_front_end_on_file(
{"test", "decompiler", "reference", "all_forward_declarations.gc"});
Timer timer;
int total_lines = 0;