mirror of
https://github.com/open-goal/jak-project
synced 2026-08-18 13:53:01 -04:00
[Decomp] Decompile gstring (#267)
* decompile gstring * update * Update code_status.md * Update code_status.md * decompile gstate * add test for states, hope it passes * also test throw and catch xmms * update doc
This commit is contained in:
@@ -285,6 +285,14 @@ goos::Object Break::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
std::string EmptyVtx::to_string() const {
|
||||
return "empty";
|
||||
}
|
||||
|
||||
goos::Object EmptyVtx::to_form() const {
|
||||
return pretty_print::build_list("empty");
|
||||
}
|
||||
|
||||
ControlFlowGraph::ControlFlowGraph() {
|
||||
// allocate the entry and exit vertices.
|
||||
m_entry = alloc<EntryVtx>();
|
||||
@@ -1100,6 +1108,7 @@ bool ControlFlowGraph::find_cond_w_else() {
|
||||
}
|
||||
|
||||
if (!is_found_after(else_block, b0)) {
|
||||
;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1228,6 +1237,185 @@ bool ControlFlowGraph::find_cond_w_else() {
|
||||
return found;
|
||||
}
|
||||
|
||||
bool ControlFlowGraph::find_cond_w_empty_else() {
|
||||
bool found = false;
|
||||
|
||||
for_each_top_level_vtx([&](CfgVtx* vtx) {
|
||||
// determine where the "else" block would be
|
||||
auto* c0 = vtx; // first condition
|
||||
auto* b0 = c0->next; // first body
|
||||
if (!b0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// printf("cwe try %s %s\n", c0->to_string().c_str(), b0->to_string().c_str());
|
||||
|
||||
// first condition should have the _option_ to fall through to first body
|
||||
if (c0->succ_ft != b0 || c0->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// first body MUST unconditionally jump to else
|
||||
if (b0->succ_ft || b0->end_branch.branch_likely ||
|
||||
b0->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (b0->pred.size() != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(b0->end_branch.has_branch);
|
||||
assert(b0->end_branch.branch_always);
|
||||
assert(b0->succ_branch);
|
||||
|
||||
// TODO - check what's in the delay slot!
|
||||
auto* end_block = b0->succ_branch;
|
||||
if (!end_block) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_found_after(end_block, b0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* else_block = end_block->prev;
|
||||
if (!else_block) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (else_block != b0) {
|
||||
return true;
|
||||
} else {
|
||||
else_block = end_block;
|
||||
}
|
||||
|
||||
if (else_block->succ_branch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!else_block->end_branch.has_branch);
|
||||
|
||||
std::vector<CondWithElse::Entry> entries = {{c0, b0}};
|
||||
auto* prev_condition = c0;
|
||||
auto* prev_body = b0;
|
||||
|
||||
// loop to try to grab all the cases up to the else, or reject if the inside is not sufficiently
|
||||
// compact or if this is not actually a cond with else Note, we are responsible for checking the
|
||||
// branch of prev_condition, but not the fallthrough
|
||||
while (true) {
|
||||
auto* next = prev_body->next;
|
||||
if (next == else_block) {
|
||||
// TODO - check what's in the delay slot!
|
||||
// we're done!
|
||||
// check the prev_condition, prev_body blocks properly go to the else/end_block
|
||||
// prev_condition should jump to else:
|
||||
if (prev_condition->succ_branch != else_block || prev_condition->end_branch.branch_likely ||
|
||||
prev_condition->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// prev_body should jump to end
|
||||
if (prev_body->succ_branch != end_block ||
|
||||
prev_body->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
auto* c = next;
|
||||
auto* b = c->next;
|
||||
if (!c || !b) {
|
||||
;
|
||||
return true;
|
||||
};
|
||||
// attempt to add another
|
||||
|
||||
if (c->pred.size() != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (b->pred.size() != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// how to get to cond
|
||||
if (prev_condition->succ_branch != c || prev_condition->end_branch.branch_likely ||
|
||||
prev_condition->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prev_body->end_branch.kind != CfgVtx::DelaySlotKind::NOP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c->succ_ft != b) {
|
||||
return true; // condition should have the option to fall through if matched
|
||||
}
|
||||
|
||||
// TODO - check what's in the delay slot!
|
||||
if (c->end_branch.branch_likely) {
|
||||
return true; // otherwise should go to next with a non-likely branch
|
||||
}
|
||||
|
||||
if (b->succ_ft || b->end_branch.branch_likely) {
|
||||
return true; // body should go straight to else
|
||||
}
|
||||
|
||||
if (b->succ_branch != end_block) {
|
||||
return true;
|
||||
}
|
||||
|
||||
entries.emplace_back(c, b);
|
||||
prev_body = b;
|
||||
prev_condition = c;
|
||||
}
|
||||
}
|
||||
|
||||
// now we need to add it
|
||||
// printf("got cwe\n");
|
||||
auto new_cwe = alloc<CondWithElse>();
|
||||
|
||||
// link x <-> new_cwe
|
||||
for (auto* npred : c0->pred) {
|
||||
npred->replace_succ_and_check(c0, new_cwe);
|
||||
}
|
||||
new_cwe->pred = c0->pred;
|
||||
new_cwe->prev = c0->prev;
|
||||
if (new_cwe->prev) {
|
||||
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);
|
||||
to_replace.push_back(entries.back().condition);
|
||||
for (const auto& x : entries) {
|
||||
to_replace.push_back(x.body);
|
||||
}
|
||||
end_block->replace_preds_with_and_check(to_replace, new_cwe);
|
||||
new_cwe->succ_ft = end_block;
|
||||
new_cwe->next = end_block;
|
||||
end_block->prev = new_cwe;
|
||||
|
||||
// new_cwe->else_vtx = else_block;
|
||||
new_cwe->else_vtx = alloc<EmptyVtx>();
|
||||
new_cwe->entries = std::move(entries);
|
||||
|
||||
new_cwe->else_vtx->parent_claim(new_cwe);
|
||||
for (const auto& x : new_cwe->entries) {
|
||||
x.body->parent_claim(new_cwe);
|
||||
x.condition->parent_claim(new_cwe);
|
||||
}
|
||||
found = true;
|
||||
return false;
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
#define printf(format, ...) ;
|
||||
|
||||
bool ControlFlowGraph::find_cond_n_else() {
|
||||
@@ -1889,6 +2077,12 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
||||
if (!changed) {
|
||||
changed = changed || cfg->find_goto_not_end();
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
changed = changed || cfg->find_cond_w_empty_else();
|
||||
if (changed) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfg->is_fully_resolved()) {
|
||||
|
||||
@@ -270,6 +270,12 @@ class Break : public CfgVtx {
|
||||
CfgVtx* unreachable_block = nullptr;
|
||||
};
|
||||
|
||||
class EmptyVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
};
|
||||
|
||||
struct BasicBlock;
|
||||
|
||||
/*!
|
||||
@@ -294,6 +300,7 @@ class ControlFlowGraph {
|
||||
void link_fall_through_likely(BlockVtx* first, BlockVtx* second, std::vector<BasicBlock>& blocks);
|
||||
void link_branch(BlockVtx* first, BlockVtx* second, std::vector<BasicBlock>& blocks);
|
||||
bool find_cond_w_else();
|
||||
bool find_cond_w_empty_else();
|
||||
bool find_cond_n_else();
|
||||
|
||||
// bool find_if_else_top_level();
|
||||
|
||||
@@ -197,7 +197,7 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
return pool.alloc_element<SetFormFormElement>(addr, val);
|
||||
}
|
||||
|
||||
if (input_type.typespec() == TypeSpec("pointer")) {
|
||||
if (input_type.typespec() == TypeSpec("pointer") && ro.offset == 0) {
|
||||
std::string cast_type;
|
||||
switch (m_size) {
|
||||
case 1:
|
||||
|
||||
@@ -540,11 +540,12 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
// remember that we're an object new.
|
||||
return TP_Type::make_object_new(method_type);
|
||||
}
|
||||
if (method_id == GOAL_NEW_METHOD ||
|
||||
input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
|
||||
if (method_id == GOAL_NEW_METHOD) {
|
||||
return TP_Type::make_from_ts(method_type);
|
||||
} else if (input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
|
||||
return TP_Type::make_non_virtual_method(method_type);
|
||||
} else {
|
||||
return TP_Type::make_method(method_type);
|
||||
return TP_Type::make_virtual_method(method_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,7 +556,8 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
auto method_info = dts.ts.lookup_method("object", method_id);
|
||||
if (method_id != GOAL_NEW_METHOD && method_id != GOAL_RELOC_METHOD) {
|
||||
// this can get us the wrong thing for `new` methods. And maybe relocate?
|
||||
return TP_Type::make_from_ts(method_info.type.substitute_for_method_call("object"));
|
||||
return TP_Type::make_non_virtual_method(
|
||||
method_info.type.substitute_for_method_call("object"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,7 +826,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
|
||||
for (uint32_t i = 0; i < in_type.arg_count() - 1; i++) {
|
||||
m_read_regs.emplace_back(Reg::GPR, arg_regs[i]);
|
||||
m_arg_vars.push_back(Variable(VariableMode::READ, m_read_regs.back(), m_my_idx));
|
||||
if (i == 0 && in_tp.kind == TP_Type::Kind::METHOD) {
|
||||
if (i == 0 && in_tp.kind == TP_Type::Kind::VIRTUAL_METHOD) {
|
||||
m_read_regs.pop_back();
|
||||
m_arg_vars.pop_back();
|
||||
m_is_virtual_method = true;
|
||||
|
||||
@@ -127,7 +127,8 @@ class SimpleExpressionElement : public FormElement {
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects);
|
||||
bool allow_side_effects,
|
||||
bool reverse);
|
||||
void update_from_stack_force_ui_2(const Env& env,
|
||||
FixedOperatorKind kind,
|
||||
FormPool& pool,
|
||||
|
||||
@@ -577,7 +577,8 @@ void SimpleExpressionElement::update_from_stack_force_si_2(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
bool allow_side_effects,
|
||||
bool reverse) {
|
||||
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
bool arg1_i = true;
|
||||
bool arg1_reg = m_expr.get_arg(1).is_var();
|
||||
@@ -589,8 +590,17 @@ void SimpleExpressionElement::update_from_stack_force_si_2(const Env& env,
|
||||
|
||||
std::vector<Form*> args;
|
||||
if (arg1_reg) {
|
||||
args = pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var()}, env, pool, stack,
|
||||
allow_side_effects);
|
||||
if (reverse) {
|
||||
args = pop_to_forms({m_expr.get_arg(1).var(), m_expr.get_arg(0).var()}, env, pool, stack,
|
||||
allow_side_effects);
|
||||
auto temp = args.at(1);
|
||||
args.at(1) = args.at(0);
|
||||
args.at(0) = temp;
|
||||
} else {
|
||||
args = pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var()}, env, pool, stack,
|
||||
allow_side_effects);
|
||||
}
|
||||
|
||||
} else {
|
||||
args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects);
|
||||
args.push_back(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
|
||||
@@ -807,19 +817,19 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
|
||||
break;
|
||||
case SimpleExpression::Kind::DIV_SIGNED:
|
||||
update_from_stack_force_si_2(env, FixedOperatorKind::DIVISION, pool, stack, result,
|
||||
allow_side_effects);
|
||||
allow_side_effects, false);
|
||||
break;
|
||||
case SimpleExpression::Kind::MOD_SIGNED:
|
||||
update_from_stack_force_si_2(env, FixedOperatorKind::MOD, pool, stack, result,
|
||||
allow_side_effects);
|
||||
allow_side_effects, false);
|
||||
break;
|
||||
case SimpleExpression::Kind::MIN_SIGNED:
|
||||
update_from_stack_force_si_2(env, FixedOperatorKind::MIN, pool, stack, result,
|
||||
allow_side_effects);
|
||||
allow_side_effects, false);
|
||||
break;
|
||||
case SimpleExpression::Kind::MAX_SIGNED:
|
||||
update_from_stack_force_si_2(env, FixedOperatorKind::MAX, pool, stack, result,
|
||||
allow_side_effects);
|
||||
allow_side_effects, false);
|
||||
break;
|
||||
case SimpleExpression::Kind::AND:
|
||||
update_from_stack_copy_first_int_2(env, FixedOperatorKind::LOGAND, pool, stack, result,
|
||||
@@ -987,7 +997,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
bool is_method = false;
|
||||
auto& tp_type = env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg());
|
||||
if (env.has_type_analysis()) {
|
||||
if (tp_type.kind == TP_Type::Kind::METHOD && all_pop_vars.size() >= 1) {
|
||||
if (tp_type.kind == TP_Type::Kind::VIRTUAL_METHOD && all_pop_vars.size() >= 1) {
|
||||
is_method = true;
|
||||
}
|
||||
function_type = tp_type.typespec();
|
||||
@@ -1002,7 +1012,15 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
// all_pop_vars.erase(all_pop_vars.begin() + 1);
|
||||
// }
|
||||
|
||||
if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) {
|
||||
std::swap(all_pop_vars.at(0), all_pop_vars.at(1));
|
||||
}
|
||||
auto unstacked = pop_to_forms(all_pop_vars, env, pool, stack, allow_side_effects);
|
||||
if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) {
|
||||
std::swap(unstacked.at(0), unstacked.at(1));
|
||||
std::swap(all_pop_vars.at(0), all_pop_vars.at(1));
|
||||
}
|
||||
|
||||
std::vector<Form*> arg_forms;
|
||||
|
||||
for (size_t arg_id = 0; arg_id < nargs; arg_id++) {
|
||||
@@ -1200,7 +1218,12 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
// temp_form->to_string(env));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Method call detected, not yet implemented");
|
||||
auto ti = env.dts->ts.lookup_type(type_1);
|
||||
auto is_basic = dynamic_cast<BasicType*>(ti);
|
||||
if (!is_basic) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Method call detected, not yet implemented {} {}\n", name, type_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1743,24 +1766,47 @@ void ConditionElement::update_from_stack(const Env& env,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
std::vector<Form*> source_forms;
|
||||
std::vector<Form*> source_forms, popped_forms;
|
||||
std::vector<TypeSpec> source_types;
|
||||
std::vector<Variable> vars;
|
||||
|
||||
for (int i = 0; i < get_condition_num_args(m_kind); i++) {
|
||||
auto& var = m_src[i]->var();
|
||||
vars.push_back(var);
|
||||
source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec());
|
||||
if (m_src[i]->is_var()) {
|
||||
auto& var = m_src[i]->var();
|
||||
vars.push_back(var);
|
||||
source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec());
|
||||
} else if (m_src[i]->is_int()) {
|
||||
if (m_src[i]->get_int() == 0 && condition_uses_float(m_kind)) {
|
||||
// if we're doing a floating point comparison, and one of our arguments is a constant
|
||||
// which is an "integer zero", treat it as a floating point zero.
|
||||
source_types.push_back(TypeSpec("float"));
|
||||
} else {
|
||||
source_types.push_back(TypeSpec("int"));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported atom in ConditionElement::push_to_stack");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_flipped) {
|
||||
std::reverse(vars.begin(), vars.end());
|
||||
}
|
||||
source_forms = pop_to_forms(vars, env, pool, stack, allow_side_effects, m_consumed);
|
||||
|
||||
popped_forms = pop_to_forms(vars, env, pool, stack, allow_side_effects, m_consumed);
|
||||
if (m_flipped) {
|
||||
std::reverse(source_forms.begin(), source_forms.end());
|
||||
std::reverse(popped_forms.begin(), popped_forms.end());
|
||||
}
|
||||
|
||||
int popped_counter = 0;
|
||||
for (int i = 0; i < get_condition_num_args(m_kind); i++) {
|
||||
if (m_src[i]->is_var()) {
|
||||
source_forms.push_back(popped_forms.at(popped_counter++));
|
||||
} else {
|
||||
source_forms.push_back(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, *m_src[i]));
|
||||
}
|
||||
}
|
||||
assert(popped_counter == int(popped_forms.size()));
|
||||
assert(source_forms.size() == source_types.size());
|
||||
|
||||
result->push_back(make_generic(env, pool, source_forms, source_types));
|
||||
}
|
||||
|
||||
|
||||
@@ -1463,6 +1463,8 @@ Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
nullptr, cfg_to_ir(pool, f, cvtx->body), cfg_to_ir(pool, f, cvtx->unreachable_block));
|
||||
clean_up_break(pool, dynamic_cast<BreakElement*>(result->try_as_single_element()));
|
||||
return result;
|
||||
} else if (dynamic_cast<const EmptyVtx*>(vtx)) {
|
||||
return pool.alloc_single_element_form<EmptyElement>(nullptr);
|
||||
}
|
||||
|
||||
throw std::runtime_error("not yet implemented IR conversion.");
|
||||
|
||||
@@ -95,6 +95,23 @@ std::string final_defun_out(const Function& func,
|
||||
append(top_form, pretty_print::build_list(inline_body));
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::UNIDENTIFIED) {
|
||||
std::string def_name = "defun-anon";
|
||||
assert(special_mode == FunctionDefSpecials::NONE);
|
||||
std::vector<goos::Object> top;
|
||||
top.push_back(pretty_print::to_symbol(def_name));
|
||||
top.push_back(pretty_print::to_symbol(func.guessed_name.to_string()));
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
if (var_count > 0) {
|
||||
append(top_form, pretty_print::build_list(var_dec));
|
||||
}
|
||||
|
||||
append(top_form, pretty_print::build_list(inline_body));
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
return "nyi";
|
||||
}
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@
|
||||
(declare-type cpu-thread basic)
|
||||
(declare-type state basic)
|
||||
(declare-type dead-pool basic)
|
||||
(declare-type event-message-block structure)
|
||||
|
||||
;; gkernel-h
|
||||
(deftype thread (basic)
|
||||
@@ -280,9 +281,9 @@
|
||||
(state state :offset-assert #x38)
|
||||
(trans-hook function :offset-assert #x3c)
|
||||
(post-hook function :offset-assert #x40)
|
||||
(event-hook (function stack-frame (function object) function state object) :offset-assert #x44)
|
||||
(event-hook (function basic int basic event-message-block object) :offset-assert #x44)
|
||||
(allocated-length int32 :offset-assert #x48)
|
||||
(next-state basic :offset-assert #x4c)
|
||||
(next-state state :offset-assert #x4c)
|
||||
(heap-base pointer :offset-assert #x50)
|
||||
(heap-top pointer :offset-assert #x54)
|
||||
(heap-cur pointer :offset-assert #x58)
|
||||
@@ -425,11 +426,14 @@
|
||||
(trans (function object) :offset-assert 20)
|
||||
(post function :offset-assert 24)
|
||||
(enter (function object object object object object object object) :offset-assert 28)
|
||||
(event basic :offset-assert 32)
|
||||
(event (function basic int basic event-message-block object) :offset-assert 32)
|
||||
)
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (code function)
|
||||
(trans function) (enter function) (exit (function object)) (event function)) _type_ 0)
|
||||
(trans (function object))
|
||||
(enter (function object object object object object object object))
|
||||
(exit (function object))
|
||||
(event (function basic int basic event-message-block object))) _type_ 0)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x24
|
||||
@@ -631,7 +635,7 @@
|
||||
(define-extern copy-charp<-charp (function (pointer uint8) (pointer uint8) (pointer uint8)))
|
||||
(define-extern cat-string<-string (function string string string))
|
||||
(define-extern catn-string<-charp (function string (pointer uint8) int string))
|
||||
(define-extern cat-string<-string_to_charp (function string string int (pointer uint8)))
|
||||
(define-extern cat-string<-string_to_charp (function string string (pointer uint8) (pointer uint8)))
|
||||
(define-extern append-character-to-string (function string uint8 int))
|
||||
(define-extern charp-basename (function (pointer uint8) (pointer uint8)))
|
||||
(define-extern clear (function string string))
|
||||
@@ -639,9 +643,9 @@
|
||||
(define-extern string>? (function string string symbol))
|
||||
(define-extern string<=? (function string string symbol))
|
||||
(define-extern string>=? (function string string symbol))
|
||||
(define-extern string-skip-to-char (function (pointer uint8) uint8 (pointer uint8)))
|
||||
(define-extern string-skip-to-char (function (pointer uint8) uint (pointer uint8)))
|
||||
;; this one might be wrong
|
||||
(define-extern string-cat-to-last-char (function uint8 string uint8 (pointer uint8)))
|
||||
(define-extern string-cat-to-last-char (function string string uint (pointer uint8)))
|
||||
(define-extern string-skip-whitespace (function (pointer uint8) (pointer uint8)))
|
||||
(define-extern string-suck-up! (function string (pointer uint8) symbol))
|
||||
(define-extern string-strip-leading-whitespace! (function string symbol))
|
||||
@@ -689,7 +693,7 @@
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(define-extern looping-code (function symbol))
|
||||
(define-extern send-event-function (function process state object))
|
||||
(define-extern send-event-function (function process event-message-block object))
|
||||
(define-extern enter-state (function object object object object object object object))
|
||||
(define-extern inherit-state (function state state state))
|
||||
|
||||
|
||||
@@ -6,5 +6,9 @@
|
||||
[28, "(function process symbol)"],
|
||||
[30, "(function process symbol)"],
|
||||
[32, "(function process symbol)"]
|
||||
],
|
||||
|
||||
"hud":[
|
||||
[15, "(function basic int basic event-message-block object)"]
|
||||
]
|
||||
}
|
||||
@@ -105,6 +105,19 @@
|
||||
"name=":[
|
||||
[24, ["a1", "symbol"]],
|
||||
[39, ["a0", "symbol"]]
|
||||
]
|
||||
],
|
||||
|
||||
"string-cat-to-last-char":[
|
||||
[3, ["s5", "(pointer uint8)"]],
|
||||
[4, ["s5", "string"]]
|
||||
],
|
||||
|
||||
"string-strip-trailing-whitespace!":[
|
||||
[15, ["v1", "(pointer uint8)"]]
|
||||
],
|
||||
|
||||
"(anon-function 15 hud)":[
|
||||
[0, ["s6", "hud"]]
|
||||
]
|
||||
|
||||
}
|
||||
@@ -236,6 +236,110 @@
|
||||
|
||||
"box-vector-inside?":{
|
||||
"args":["box", "pt"]
|
||||
},
|
||||
|
||||
"string=":{
|
||||
"args":["str-a", "str-b"],
|
||||
"vars":{"a2-0":"a-ptr", "v1-0":"b-ptr"}
|
||||
},
|
||||
|
||||
"string-charp=":{
|
||||
"args":["str", "charp"],
|
||||
"vars":{"v1-0":"str-ptr"}
|
||||
},
|
||||
|
||||
"copyn-string<-charp":{
|
||||
"args":["str", "charp", "len"],
|
||||
"vars":{"a3-0":"i", "v1-0":"str-ptr"}
|
||||
},
|
||||
|
||||
"string<-charp":{
|
||||
"args":["str", "charp"],
|
||||
"vars":{"v1-0":"str-ptr"}
|
||||
},
|
||||
|
||||
"charp<-string":{
|
||||
"args":["charp", "str"],
|
||||
"vars":{"v1-0":"str-ptr"}
|
||||
},
|
||||
|
||||
"copy-charp<-charp":{
|
||||
"args":["dst", "src"]
|
||||
},
|
||||
|
||||
"cat-string<-string":{
|
||||
"args":["a", "b"],
|
||||
"vars":{"v1-0":"a-ptr", "a1-1":"b-ptr"}
|
||||
},
|
||||
|
||||
"catn-string<-charp":{
|
||||
"args":["a", "b", "len"],
|
||||
"vars":{"v1-0":"a-ptr", "a3-2":"i"}
|
||||
},
|
||||
|
||||
"cat-string<-string_to_charp":{
|
||||
"args":["a", "b", "end-ptr"],
|
||||
"vars":{"v1-0":"b-ptr", "v0-0":"a-ptr"}
|
||||
},
|
||||
|
||||
"append-character-to-string":{
|
||||
"args":["str", "char"],
|
||||
"vars":{"v1-0":"str-ptr"}
|
||||
},
|
||||
|
||||
"charp-basename":{
|
||||
"args":["charp"],
|
||||
"vars":{"v1-0":"ptr"}
|
||||
},
|
||||
|
||||
"string<?":{
|
||||
"args":["a", "b"],
|
||||
"vars":{"s4-1":"len", "v1-4":"i"}
|
||||
},
|
||||
|
||||
"string>?":{
|
||||
"args":["a", "b"],
|
||||
"vars":{"s4-1":"len", "v1-4":"i"}
|
||||
},
|
||||
"string<=?":{
|
||||
"args":["a", "b"],
|
||||
"vars":{"s4-1":"len", "v1-4":"i"}
|
||||
},
|
||||
"string>=?":{
|
||||
"args":["a", "b"],
|
||||
"vars":{"s4-1":"len", "v1-4":"i"}
|
||||
},
|
||||
"string-cat-to-last-char":{
|
||||
"args":["base-str", "append-str", "char"],
|
||||
"vars":{"s4-0":"end-of-append", "v1-0":"location-of-char"}
|
||||
},
|
||||
"string-suck-up!":{
|
||||
"args":["str", "location"],
|
||||
"vars":{"v1-2":"str-ptr"}
|
||||
},
|
||||
"string-strip-trailing-whitespace!":{
|
||||
"args":["str"],
|
||||
"vars":{"v1-6":"ptr"}
|
||||
},
|
||||
|
||||
"string-get-arg!!":{
|
||||
"args":["a-str", "arg"],
|
||||
"vars":{"s4-0":"arg-word-start", "s4-1":"arg-end", "v1-3":"arg-start"}
|
||||
},
|
||||
|
||||
"string->int":{
|
||||
"args":["str"],
|
||||
"vars":{"a0-1":"str-ptr", "v0-0":"result",
|
||||
"a0-2":"next-char-1","a0-3":"next-char-2"}
|
||||
},
|
||||
|
||||
"string-get-flag!!":{
|
||||
"args":["result", "in", "first-flag", "second-flag"]
|
||||
},
|
||||
|
||||
"(method 0 state)":{
|
||||
"args":["allocation", "type-to-make", "name", "code", "trans", "enter", "exit", "event"],
|
||||
"vars":{"v0-0":"obj"}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -277,7 +277,13 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
||||
*changed = true;
|
||||
return TP_Type::make_from_ts("int");
|
||||
|
||||
case TP_Type::Kind::METHOD:
|
||||
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:
|
||||
// never allow this to remain method
|
||||
*changed = true;
|
||||
return TP_Type::make_from_ts(
|
||||
|
||||
@@ -55,7 +55,9 @@ std::string TP_Type::print() const {
|
||||
return fmt::format("<integer {} + ({} x {})>", m_int, m_extra_multiplier, m_ts.print());
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return fmt::format("<dynamic-method-access>");
|
||||
case Kind::METHOD:
|
||||
case Kind::VIRTUAL_METHOD:
|
||||
return fmt::format("<vmethod {}>", m_ts.print());
|
||||
case Kind::NON_VIRTUAL_METHOD:
|
||||
return fmt::format("<method {}>", m_ts.print());
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
@@ -75,7 +77,9 @@ bool TP_Type::operator==(const TP_Type& other) const {
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::TYPE_OF_TYPE_NO_VIRTUAL:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::METHOD:
|
||||
case Kind::VIRTUAL_METHOD:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::NON_VIRTUAL_METHOD:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::FALSE_AS_NULL:
|
||||
return true;
|
||||
@@ -144,7 +148,9 @@ TypeSpec TP_Type::typespec() const {
|
||||
return TypeSpec("object");
|
||||
case Kind::FORMAT_STRING:
|
||||
return TypeSpec("string");
|
||||
case Kind::METHOD:
|
||||
case Kind::VIRTUAL_METHOD:
|
||||
return m_ts;
|
||||
case Kind::NON_VIRTUAL_METHOD:
|
||||
return m_ts;
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
|
||||
@@ -29,7 +29,8 @@ class TP_Type {
|
||||
INTEGER_CONSTANT_PLUS_VAR, // constant + variable. for dynamic addr of
|
||||
INTEGER_CONSTANT_PLUS_VAR_MULT, // like var + 100 + 12 * var2
|
||||
DYNAMIC_METHOD_ACCESS, // partial access into a
|
||||
METHOD,
|
||||
VIRTUAL_METHOD,
|
||||
NON_VIRTUAL_METHOD,
|
||||
INVALID
|
||||
} kind = Kind::UNINITIALIZED;
|
||||
TP_Type() = default;
|
||||
@@ -75,9 +76,16 @@ class TP_Type {
|
||||
|
||||
static TP_Type make_from_ts(const std::string& ts) { return make_from_ts(TypeSpec(ts)); }
|
||||
|
||||
static TP_Type make_method(const TypeSpec& method_type) {
|
||||
static TP_Type make_virtual_method(const TypeSpec& method_type) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::METHOD;
|
||||
result.kind = Kind::VIRTUAL_METHOD;
|
||||
result.m_ts = method_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_non_virtual_method(const TypeSpec& method_type) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::NON_VIRTUAL_METHOD;
|
||||
result.m_ts = method_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user