2020-09-29 20:24:15 -04:00
|
|
|
#include "DecompilerTypeSystem.h"
|
|
|
|
#include "common/goos/Reader.h"
|
2021-04-19 20:29:38 -04:00
|
|
|
#include "common/type_system/defenum.h"
|
2020-09-29 20:24:15 -04:00
|
|
|
#include "common/type_system/deftype.h"
|
2020-11-27 16:38:36 -05:00
|
|
|
#include "decompiler/Disasm/Register.h"
|
2021-01-06 12:16:39 -05:00
|
|
|
#include "common/log/log.h"
|
2020-11-28 15:35:38 -05:00
|
|
|
#include "TP_Type.h"
|
2020-09-29 20:24:15 -04:00
|
|
|
|
2021-01-06 20:04:15 -05:00
|
|
|
namespace decompiler {
|
2020-09-29 20:24:15 -04:00
|
|
|
DecompilerTypeSystem::DecompilerTypeSystem() {
|
|
|
|
ts.add_builtin_types();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// some utilities for parsing the type def file
|
|
|
|
|
|
|
|
goos::Object& car(goos::Object& pair) {
|
|
|
|
if (pair.is_pair()) {
|
|
|
|
return pair.as_pair()->car;
|
|
|
|
} else {
|
2021-05-06 18:47:32 -04:00
|
|
|
throw std::runtime_error("car called on something that was not a pair: " + pair.print());
|
2020-09-29 20:24:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
goos::Object& cdr(goos::Object& pair) {
|
|
|
|
if (pair.is_pair()) {
|
|
|
|
return pair.as_pair()->cdr;
|
|
|
|
} else {
|
2021-05-06 18:47:32 -04:00
|
|
|
throw std::runtime_error("cdr called on something that was not a pair");
|
2020-09-29 20:24:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void for_each_in_list(goos::Object& list, T f) {
|
|
|
|
goos::Object* iter = &list;
|
|
|
|
while (iter->is_pair()) {
|
|
|
|
f(car(*iter));
|
|
|
|
iter = &cdr(*iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!iter->is_empty_list()) {
|
|
|
|
throw std::runtime_error("malformed list");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_path) {
|
2020-12-17 15:48:07 -05:00
|
|
|
auto read = m_reader.read_from_file(file_path);
|
2022-03-01 20:15:15 -05:00
|
|
|
auto& data = cdr(read);
|
2020-09-29 20:24:15 -04:00
|
|
|
|
|
|
|
for_each_in_list(data, [&](goos::Object& o) {
|
2021-02-09 20:59:14 -05:00
|
|
|
try {
|
|
|
|
if (car(o).as_symbol()->name == "define-extern") {
|
|
|
|
auto* rest = &cdr(o);
|
|
|
|
auto sym_name = car(*rest);
|
|
|
|
rest = &cdr(*rest);
|
|
|
|
auto sym_type = car(*rest);
|
|
|
|
if (!cdr(*rest).is_empty_list()) {
|
|
|
|
throw std::runtime_error("malformed define-extern");
|
|
|
|
}
|
|
|
|
add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type));
|
2020-09-29 20:24:15 -04:00
|
|
|
|
2021-02-09 20:59:14 -05:00
|
|
|
} else if (car(o).as_symbol()->name == "deftype") {
|
|
|
|
auto dtr = parse_deftype(cdr(o), &ts);
|
2021-04-22 19:08:58 -04:00
|
|
|
if (dtr.create_runtime_type) {
|
|
|
|
add_symbol(dtr.type.base_type(), "type");
|
|
|
|
}
|
2021-10-16 14:06:33 -04:00
|
|
|
// declare the type's states globally
|
|
|
|
for (auto& state : dtr.type_info->get_states_declared_for_type()) {
|
2021-10-21 22:38:39 -04:00
|
|
|
add_symbol(state.first, state.second);
|
2021-10-16 14:06:33 -04:00
|
|
|
}
|
2021-04-22 19:08:58 -04:00
|
|
|
|
2021-02-09 20:59:14 -05:00
|
|
|
} else if (car(o).as_symbol()->name == "declare-type") {
|
|
|
|
auto* rest = &cdr(o);
|
|
|
|
auto type_name = car(*rest);
|
|
|
|
rest = &cdr(*rest);
|
|
|
|
auto type_kind = car(*rest);
|
|
|
|
if (!cdr(*rest).is_empty_list()) {
|
|
|
|
throw std::runtime_error("malformed declare-type");
|
|
|
|
}
|
2021-06-15 21:03:55 -04:00
|
|
|
ts.forward_declare_type_as(type_name.as_symbol()->name, type_kind.as_symbol()->name);
|
2021-04-19 20:29:38 -04:00
|
|
|
} else if (car(o).as_symbol()->name == "defenum") {
|
2021-04-22 19:08:58 -04:00
|
|
|
parse_defenum(cdr(o), &ts);
|
|
|
|
// so far, enums are never runtime types so there's no symbol for them.
|
2020-10-25 12:07:10 -04:00
|
|
|
} else {
|
2021-02-09 20:59:14 -05:00
|
|
|
throw std::runtime_error("Decompiler cannot parse " + car(o).print());
|
2020-10-25 12:07:10 -04:00
|
|
|
}
|
2021-02-09 20:59:14 -05:00
|
|
|
} catch (std::exception& e) {
|
|
|
|
auto info = m_reader.db.get_info_for(o);
|
2021-05-11 16:43:13 -04:00
|
|
|
lg::error("{} when parsing decompiler type file:\n{}", e.what(), info);
|
2021-02-09 20:59:14 -05:00
|
|
|
throw e;
|
2020-09-29 20:24:15 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-03-13 16:10:39 -05:00
|
|
|
TypeSpec DecompilerTypeSystem::parse_type_spec(const std::string& str) const {
|
2020-12-17 15:48:07 -05:00
|
|
|
auto read = m_reader.read_from_string(str);
|
|
|
|
auto data = cdr(read);
|
|
|
|
return parse_typespec(&ts, car(data));
|
|
|
|
}
|
|
|
|
|
2020-09-29 20:24:15 -04:00
|
|
|
std::string DecompilerTypeSystem::dump_symbol_types() {
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(symbol_add_order.size() == symbols.size());
|
2020-09-29 20:24:15 -04:00
|
|
|
std::string result;
|
|
|
|
for (auto& symbol_name : symbol_add_order) {
|
|
|
|
auto skv = symbol_types.find(symbol_name);
|
|
|
|
if (skv == symbol_types.end()) {
|
|
|
|
result += fmt::format(";;(define-extern {} object) ;; unknown type\n", symbol_name);
|
|
|
|
} else {
|
|
|
|
result += fmt::format("(define-extern {} {})\n", symbol_name, skv->second.print());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2020-10-24 22:51:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void DecompilerTypeSystem::add_type_flags(const std::string& name, u64 flags) {
|
|
|
|
auto kv = type_flags.find(name);
|
|
|
|
if (kv != type_flags.end()) {
|
|
|
|
if (kv->second != flags) {
|
2021-01-06 12:16:39 -05:00
|
|
|
lg::warn("duplicated type flags for {}, was 0x{:x}, now 0x{:x}", name.c_str(), kv->second,
|
|
|
|
flags);
|
|
|
|
lg::warn("duplicated type flags that are inconsistent!");
|
2020-10-24 22:51:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
type_flags[name] = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecompilerTypeSystem::add_type_parent(const std::string& child, const std::string& parent) {
|
|
|
|
auto kv = type_parents.find(child);
|
|
|
|
if (kv != type_parents.end()) {
|
|
|
|
if (kv->second != parent) {
|
2021-01-06 12:16:39 -05:00
|
|
|
lg::warn("duplicated type parents for {} was {} now {}", child.c_str(), kv->second.c_str(),
|
|
|
|
parent.c_str());
|
2020-10-24 22:51:40 -04:00
|
|
|
throw std::runtime_error("duplicated type parents that are inconsistent!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
type_parents[child] = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string DecompilerTypeSystem::lookup_parent_from_inspects(const std::string& child) const {
|
|
|
|
auto kv_tp = type_parents.find(child);
|
|
|
|
if (kv_tp != type_parents.end()) {
|
|
|
|
return kv_tp->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DecompilerTypeSystem::lookup_flags(const std::string& type, u64* dest) const {
|
|
|
|
auto kv = type_flags.find(type);
|
|
|
|
if (kv != type_flags.end()) {
|
|
|
|
*dest = kv->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2020-10-29 21:27:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void DecompilerTypeSystem::add_symbol(const std::string& name, const TypeSpec& type_spec) {
|
|
|
|
add_symbol(name);
|
|
|
|
auto skv = symbol_types.find(name);
|
|
|
|
if (skv == symbol_types.end() || skv->second == type_spec) {
|
|
|
|
symbol_types[name] = type_spec;
|
|
|
|
} else {
|
2021-03-03 20:52:25 -05:00
|
|
|
if (ts.tc(type_spec, skv->second)) {
|
2020-10-29 21:27:52 -04:00
|
|
|
} else {
|
2021-01-06 12:16:39 -05:00
|
|
|
lg::warn("Attempting to redefine type of symbol {} from {} to {}\n", name,
|
|
|
|
skv->second.print(), type_spec.print());
|
2020-10-29 21:27:52 -04:00
|
|
|
throw std::runtime_error("Type redefinition");
|
|
|
|
}
|
|
|
|
}
|
2020-11-27 16:38:36 -05:00
|
|
|
}
|
|
|
|
|
2020-12-17 15:48:07 -05:00
|
|
|
/*!
|
|
|
|
* Compute the least common ancestor of two TP Types.
|
|
|
|
*/
|
2021-01-16 10:54:09 -05:00
|
|
|
TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
|
|
|
const TP_Type& add,
|
|
|
|
bool* changed) const {
|
2020-12-17 15:48:07 -05:00
|
|
|
// starting from most vague to most specific
|
|
|
|
|
2021-02-13 11:32:52 -05:00
|
|
|
// simplest case, no difference.
|
2020-12-17 15:48:07 -05:00
|
|
|
if (existing == add) {
|
|
|
|
*changed = false;
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
// being sometimes uninitialized should not modify types.
|
|
|
|
if (add.kind == TP_Type::Kind::UNINITIALIZED) {
|
|
|
|
*changed = false;
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace anything that's uninitialized sometimes.
|
|
|
|
if (existing.kind == TP_Type::Kind::UNINITIALIZED) {
|
|
|
|
*changed = true; // existing != none because of previous check.
|
|
|
|
return add;
|
|
|
|
}
|
|
|
|
|
|
|
|
// similar to before, false as null shouldn't modify types.
|
|
|
|
if (add.kind == TP_Type::Kind::FALSE_AS_NULL) {
|
|
|
|
*changed = false;
|
|
|
|
return existing;
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace any false as nulls.
|
|
|
|
if (existing.kind == TP_Type::Kind::FALSE_AS_NULL) {
|
|
|
|
*changed = true; // existing != false because of previous check.
|
|
|
|
return add;
|
|
|
|
}
|
|
|
|
|
|
|
|
// different values, but the same kind.
|
|
|
|
if (existing.kind == add.kind) {
|
|
|
|
switch (existing.kind) {
|
|
|
|
case TP_Type::Kind::TYPESPEC: {
|
2021-01-10 20:46:49 -05:00
|
|
|
auto new_result = TP_Type::make_from_ts(coerce_to_reg_type(ts.lowest_common_ancestor(
|
2020-12-17 15:48:07 -05:00
|
|
|
existing.get_objects_typespec(), add.get_objects_typespec())));
|
|
|
|
*changed = (new_result != existing);
|
|
|
|
return new_result;
|
2020-11-27 16:38:36 -05:00
|
|
|
}
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD: {
|
2021-02-13 16:35:27 -05:00
|
|
|
auto new_result = TP_Type::make_type_allow_virtual_object(ts.lowest_common_ancestor(
|
|
|
|
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
|
|
|
*changed = (new_result != existing);
|
|
|
|
return new_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL: {
|
|
|
|
auto new_result = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
2020-12-17 15:48:07 -05:00
|
|
|
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
|
|
|
*changed = (new_result != existing);
|
|
|
|
return new_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TP_Type::Kind::PRODUCT_WITH_CONSTANT:
|
|
|
|
// we know they are different.
|
|
|
|
*changed = true;
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("int"));
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
|
|
|
|
*changed = true;
|
|
|
|
// todo - there might be cases where we need to LCA the base types??
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("object"));
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::OBJECT_NEW_METHOD:
|
|
|
|
*changed = true;
|
|
|
|
// this case should never happen I think.
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("function"));
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::STRING_CONSTANT: {
|
|
|
|
auto existing_count = get_format_arg_count(existing.get_string());
|
|
|
|
auto added_count = get_format_arg_count(add.get_string());
|
|
|
|
*changed = true;
|
|
|
|
if (added_count == existing_count) {
|
|
|
|
return TP_Type::make_from_format_string(existing_count);
|
|
|
|
} else {
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("string"));
|
2020-11-27 16:38:36 -05:00
|
|
|
}
|
|
|
|
}
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::INTEGER_CONSTANT:
|
|
|
|
*changed = true;
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("int"));
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::FORMAT_STRING:
|
|
|
|
if (existing.get_format_string_arg_count() == add.get_format_string_arg_count()) {
|
2020-11-27 16:38:36 -05:00
|
|
|
*changed = false;
|
|
|
|
return existing;
|
2020-12-17 15:48:07 -05:00
|
|
|
} else {
|
2020-11-28 15:35:38 -05:00
|
|
|
*changed = true;
|
2021-01-10 20:46:49 -05:00
|
|
|
return TP_Type::make_from_ts(TypeSpec("string"));
|
2020-11-28 15:35:38 -05:00
|
|
|
}
|
2021-02-13 11:32:52 -05:00
|
|
|
case TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR:
|
|
|
|
if (existing.get_integer_constant() == add.get_integer_constant()) {
|
|
|
|
auto new_child = TP_Type::make_from_integer_constant_plus_var(
|
|
|
|
existing.get_integer_constant(),
|
|
|
|
coerce_to_reg_type(ts.lowest_common_ancestor(existing.get_objects_typespec(),
|
|
|
|
add.get_objects_typespec())));
|
|
|
|
*changed = (new_child != existing);
|
|
|
|
return new_child;
|
|
|
|
} else {
|
|
|
|
*changed = true;
|
|
|
|
return TP_Type::make_from_ts("int");
|
|
|
|
}
|
2020-12-17 15:48:07 -05:00
|
|
|
|
2021-02-14 18:50:45 -05:00
|
|
|
case TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
|
|
|
// a bit lazy here, but I don't think you can ever merge these.
|
|
|
|
*changed = true;
|
|
|
|
return TP_Type::make_from_ts("int");
|
|
|
|
|
2021-02-16 20:37:48 -05:00
|
|
|
case TP_Type::Kind::VIRTUAL_METHOD:
|
|
|
|
// never allow this to remain method
|
|
|
|
*changed = true;
|
|
|
|
return TP_Type::make_from_ts(
|
|
|
|
ts.lowest_common_ancestor(existing.typespec(), add.typespec()));
|
|
|
|
|
|
|
|
case TP_Type::Kind::NON_VIRTUAL_METHOD:
|
2021-02-13 16:35:27 -05:00
|
|
|
// never allow this to remain method
|
|
|
|
*changed = true;
|
|
|
|
return TP_Type::make_from_ts(
|
|
|
|
ts.lowest_common_ancestor(existing.typespec(), add.typespec()));
|
|
|
|
|
2021-06-13 13:55:55 -04:00
|
|
|
case TP_Type::Kind::LABEL_ADDR:
|
|
|
|
*changed = false;
|
|
|
|
return existing;
|
|
|
|
|
2020-12-17 15:48:07 -05:00
|
|
|
case TP_Type::Kind::FALSE_AS_NULL:
|
|
|
|
case TP_Type::Kind::UNINITIALIZED:
|
|
|
|
case TP_Type::Kind::DYNAMIC_METHOD_ACCESS:
|
|
|
|
case TP_Type::Kind::INVALID:
|
|
|
|
default:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(false);
|
2021-03-07 12:01:59 -05:00
|
|
|
return {};
|
2020-12-17 15:48:07 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// trying to combine two of different types.
|
|
|
|
if (existing.can_be_format_string() && add.can_be_format_string()) {
|
|
|
|
int existing_count = get_format_arg_count(existing);
|
|
|
|
int add_count = get_format_arg_count(add);
|
|
|
|
TP_Type result_type;
|
|
|
|
if (existing_count == add_count) {
|
|
|
|
result_type = TP_Type::make_from_format_string(existing_count);
|
|
|
|
} else {
|
2021-01-10 20:46:49 -05:00
|
|
|
result_type = TP_Type::make_from_ts(TypeSpec("string"));
|
2020-11-28 15:35:38 -05:00
|
|
|
}
|
|
|
|
|
2020-12-25 15:04:03 -05:00
|
|
|
*changed = (result_type != existing);
|
2020-12-17 15:48:07 -05:00
|
|
|
return result_type;
|
|
|
|
}
|
2020-11-27 16:38:36 -05:00
|
|
|
|
2021-02-13 16:35:27 -05:00
|
|
|
if (existing.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL &&
|
|
|
|
add.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD) {
|
|
|
|
auto result_type = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
|
|
|
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
|
|
|
*changed = (result_type != existing);
|
|
|
|
return result_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (existing.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD &&
|
|
|
|
add.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
|
|
|
|
auto result_type = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
|
|
|
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
|
|
|
*changed = (result_type != existing);
|
|
|
|
return result_type;
|
|
|
|
}
|
|
|
|
|
2020-12-17 15:48:07 -05:00
|
|
|
// otherwise, as an absolute fallback, convert both to TypeSpecs and do TypeSpec LCA
|
2021-01-10 20:46:49 -05:00
|
|
|
auto new_result = TP_Type::make_from_ts(
|
2020-12-26 11:09:59 -05:00
|
|
|
coerce_to_reg_type(ts.lowest_common_ancestor(existing.typespec(), add.typespec())));
|
2020-12-17 15:48:07 -05:00
|
|
|
*changed = (new_result != existing);
|
|
|
|
return new_result;
|
|
|
|
}
|
2020-11-28 15:35:38 -05:00
|
|
|
}
|
|
|
|
|
2020-12-17 15:48:07 -05:00
|
|
|
/*!
|
|
|
|
* Find the least common ancestor of an entire typestate.
|
|
|
|
*/
|
2020-11-27 16:38:36 -05:00
|
|
|
bool DecompilerTypeSystem::tp_lca(TypeState* combined, const TypeState& add) {
|
|
|
|
bool result = false;
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
|
|
bool diff = false;
|
|
|
|
auto new_type = tp_lca(combined->gpr_types[i], add.gpr_types[i], &diff);
|
|
|
|
if (diff) {
|
|
|
|
result = true;
|
|
|
|
combined->gpr_types[i] = new_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
|
|
bool diff = false;
|
|
|
|
auto new_type = tp_lca(combined->fpr_types[i], add.fpr_types[i], &diff);
|
|
|
|
if (diff) {
|
|
|
|
result = true;
|
|
|
|
combined->fpr_types[i] = new_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-25 14:48:54 -04:00
|
|
|
for (auto& x : add.spill_slots) {
|
|
|
|
// auto existing = combined->spill_slots.find(x.first);
|
|
|
|
// if (existing == combined->spill_slots.end()) {
|
|
|
|
// result = true;
|
|
|
|
// combined->spill_slots.insert({existing->first, existing->second});
|
|
|
|
// }
|
|
|
|
bool diff = false;
|
|
|
|
auto new_type = tp_lca(combined->spill_slots[x.first], x.second, &diff);
|
|
|
|
if (diff) {
|
|
|
|
result = true;
|
|
|
|
combined->spill_slots[x.first] = new_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 18:07:21 -04:00
|
|
|
bool diff = false;
|
|
|
|
auto new_type = tp_lca(combined->next_state_type, add.next_state_type, &diff);
|
|
|
|
if (diff) {
|
|
|
|
result = true;
|
|
|
|
combined->next_state_type = new_type;
|
|
|
|
}
|
|
|
|
|
2020-11-27 16:38:36 -05:00
|
|
|
return result;
|
2020-11-28 15:35:38 -05:00
|
|
|
}
|
2020-12-17 15:48:07 -05:00
|
|
|
|
2021-01-16 10:54:09 -05:00
|
|
|
int DecompilerTypeSystem::get_format_arg_count(const std::string& str) const {
|
2021-07-01 21:38:19 -04:00
|
|
|
auto bad_it = bad_format_strings.find(str);
|
|
|
|
if (bad_it != bad_format_strings.end()) {
|
|
|
|
return bad_it->second;
|
2021-04-25 14:48:54 -04:00
|
|
|
}
|
2021-06-19 15:50:52 -04:00
|
|
|
|
2021-09-06 20:35:03 -04:00
|
|
|
static const std::vector<char> single_char_ignore_list = {'%', 'T'};
|
|
|
|
static const std::vector<std::string> multi_char_ignore_list = {"0L", "1L", "3L", "1K",
|
|
|
|
"2j", "0k", "30L"};
|
2021-08-22 20:46:37 -04:00
|
|
|
|
2020-12-17 15:48:07 -05:00
|
|
|
int arg_count = 0;
|
|
|
|
for (size_t i = 0; i < str.length(); i++) {
|
|
|
|
if (str.at(i) == '~') {
|
|
|
|
i++; // also eat the next character.
|
2021-06-23 22:09:02 -04:00
|
|
|
|
2021-08-22 20:46:37 -04:00
|
|
|
// Check for codes that take no args
|
|
|
|
bool code_takes_no_arg = false;
|
|
|
|
for (char c : single_char_ignore_list) {
|
|
|
|
if (i < str.length() && str.at(i) == c) {
|
|
|
|
code_takes_no_arg = true;
|
|
|
|
break;
|
|
|
|
}
|
2021-06-23 22:09:02 -04:00
|
|
|
}
|
2021-06-29 20:30:52 -04:00
|
|
|
|
2021-09-06 20:35:03 -04:00
|
|
|
for (auto& code : multi_char_ignore_list) {
|
2021-08-22 20:46:37 -04:00
|
|
|
if (i + 1 < str.length() && code.length() == 2 && (str.at(i) == code.at(0)) &&
|
|
|
|
str.at(i + 1) == code.at(1)) {
|
|
|
|
code_takes_no_arg = true;
|
|
|
|
break;
|
|
|
|
}
|
2021-09-06 20:35:03 -04:00
|
|
|
if (i + 2 < str.length() && code.length() == 3 && (str.at(i) == code.at(0)) &&
|
|
|
|
str.at(i + 1) == code.at(1) && str.at(i + 2) == code.at(2)) {
|
|
|
|
code_takes_no_arg = true;
|
|
|
|
break;
|
|
|
|
}
|
2021-07-25 15:30:37 -04:00
|
|
|
}
|
|
|
|
|
2021-08-22 20:46:37 -04:00
|
|
|
if (!code_takes_no_arg) {
|
|
|
|
arg_count++;
|
2021-06-29 20:30:52 -04:00
|
|
|
}
|
2020-12-17 15:48:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return arg_count;
|
|
|
|
}
|
|
|
|
|
2021-01-16 10:54:09 -05:00
|
|
|
int DecompilerTypeSystem::get_format_arg_count(const TP_Type& type) const {
|
2020-12-17 15:48:07 -05:00
|
|
|
if (type.is_constant_string()) {
|
|
|
|
return get_format_arg_count(type.get_string());
|
|
|
|
} else {
|
|
|
|
return type.get_format_string_arg_count();
|
|
|
|
}
|
2021-01-06 20:04:15 -05:00
|
|
|
}
|
2021-03-03 20:52:25 -05:00
|
|
|
|
2021-09-06 20:35:03 -04:00
|
|
|
int DecompilerTypeSystem::get_dynamic_format_arg_count(const std::string& func_name,
|
|
|
|
int op_idx) const {
|
|
|
|
auto kv = format_ops_with_dynamic_string_by_func_name.find(func_name);
|
|
|
|
if (kv == format_ops_with_dynamic_string_by_func_name.end()) {
|
|
|
|
throw std::runtime_error(fmt::format("Unknown dynamic format string."));
|
|
|
|
} else {
|
|
|
|
auto& formats = kv->second;
|
|
|
|
auto the_format =
|
|
|
|
std::find_if(formats.begin(), formats.end(),
|
|
|
|
[op_idx](const std::vector<int> vec) { return vec.at(0) == op_idx; });
|
|
|
|
if (the_format == formats.end()) {
|
|
|
|
throw std::runtime_error(fmt::format("Unknown dynamic format string."));
|
|
|
|
}
|
|
|
|
return the_format->at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-03 20:52:25 -05:00
|
|
|
TypeSpec DecompilerTypeSystem::lookup_symbol_type(const std::string& name) const {
|
|
|
|
auto kv = symbol_types.find(name);
|
|
|
|
if (kv == symbol_types.end()) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
fmt::format("Decompiler type system did not know the type of symbol {}. Add it!", name));
|
|
|
|
} else {
|
|
|
|
return kv->second;
|
|
|
|
}
|
|
|
|
}
|
2021-07-11 18:19:41 -04:00
|
|
|
|
|
|
|
bool DecompilerTypeSystem::should_attempt_cast_simplify(const TypeSpec& expected,
|
|
|
|
const TypeSpec& actual) const {
|
|
|
|
if (expected == TypeSpec("meters") && actual == TypeSpec("float")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-06 19:32:58 -04:00
|
|
|
if (expected == TypeSpec("seconds") && actual == TypeSpec("int64")) {
|
2021-07-11 18:19:41 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expected == TypeSpec("degrees") && actual == TypeSpec("float")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !ts.tc(expected, actual);
|
|
|
|
}
|
2021-04-19 20:29:38 -04:00
|
|
|
} // namespace decompiler
|