gsc-tool/src/gsc/decompiler.cpp
2023-02-27 20:09:31 +01:00

3233 lines
110 KiB
C++

// Copyright 2023 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdinc.hpp"
#include "decompiler.hpp"
#include "context.hpp"
#include "utils/string.hpp"
namespace xsk::gsc
{
decompiler::decompiler(context const* ctx) : ctx_{ ctx }
{
}
auto decompiler::decompile(assembly const& data) -> program::ptr
{
program_ = make_program();
for (auto const& func : data.functions)
{
decompile_function(*func);
}
return std::move(program_);
}
auto decompiler::decompile_function(function const& func) -> void
{
expr_labels_.clear();
tern_labels_.clear();
in_waittill_ = false;
labels_ = func.labels;
locs_ = {};
stack_ = {};
auto loc = location{ nullptr, static_cast<location::counter_type>(func.index) };
auto name = make_expr_identifier(loc, func.name);
auto prms = make_expr_parameters(loc);
auto body = make_stmt_comp(loc, make_stmt_list(loc));
func_ = make_decl_function(loc, std::move(name), std::move(prms), std::move(body));
for (auto const& inst : func.instructions)
{
decompile_instruction(*inst);
}
if (!stack_.empty())
{
throw decomp_error("stack isn't empty at function end");
}
locs_.last = true;
locs_.end = func_->body->block->list.back().label();
func_->body->block->list.pop_back();
decompile_statements(*func_->body->block);
process_function(*func_);
program_->declarations.push_back(decl{ std::move(func_) });
}
auto decompiler::decompile_instruction(instruction const& inst) -> void
{
decompile_expressions(inst);
auto loc = location{ nullptr, static_cast<location::counter_type>(inst.index) };
switch (inst.opcode)
{
case opcode::OP_End:
{
func_->body->block->list.push_back(stmt{ make_stmt_return(loc, expr{ make_node(loc) }) });
break;
}
case opcode::OP_Return:
{
auto value = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_stmt_return(value.loc(), std::move(value)) });
break;
}
case opcode::OP_GetZero:
{
stack_.push(make_expr_integer(loc, "0"));
break;
}
case opcode::OP_GetByte:
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetUnsignedInt:
case opcode::OP_GetInteger:
case opcode::OP_GetInteger64:
{
stack_.push(make_expr_integer(loc, inst.data[0]));
break;
}
case opcode::OP_GetNegByte:
case opcode::OP_GetNegUnsignedShort:
case opcode::OP_GetNegUnsignedInt:
{
stack_.push(make_expr_integer(loc, "-" + inst.data[0]));
break;
}
case opcode::OP_GetFloat:
{
stack_.push(make_expr_float(loc, inst.data[0]));
break;
}
case opcode::OP_GetVector:
{
auto x = expr{ make_expr_float(loc, inst.data[0]) };
auto y = expr{ make_expr_float(loc, inst.data[1]) };
auto z = expr{ make_expr_float(loc, inst.data[2]) };
stack_.push(make_expr_vector(loc, std::move(x), std::move(y), std::move(z)));
break;
}
case opcode::OP_GetString:
{
stack_.push(make_expr_string(loc, inst.data[0]));
break;
}
case opcode::OP_GetIString:
{
stack_.push(make_expr_istring(loc, inst.data[0]));
break;
}
case opcode::OP_GetUndefined:
{
stack_.push(make_expr_undefined(loc));
break;
}
case opcode::OP_EmptyArray:
{
stack_.push(make_expr_empty_array(loc));
break;
}
case opcode::OP_GetLevel:
case opcode::OP_GetLevelObject:
{
stack_.push(make_expr_level(loc));
break;
}
case opcode::OP_GetAnim:
case opcode::OP_GetAnimObject:
{
stack_.push(make_expr_anim(loc));
break;
}
case opcode::OP_GetSelf:
case opcode::OP_GetSelfObject:
{
stack_.push(make_expr_self(loc));
break;
}
case opcode::OP_GetGame:
case opcode::OP_GetGameRef:
{
stack_.push(make_expr_game(loc));
break;
}
case opcode::OP_GetAnimation:
{
if (!inst.data[0].empty())
{
auto dec = make_decl_usingtree(loc, make_expr_string(loc, inst.data[0]));
program_->declarations.push_back(decl{ std::move(dec) });
}
stack_.push(make_expr_animation(loc, inst.data[1]));
break;
}
case opcode::OP_GetAnimTree:
{
if (!inst.data[0].empty())
{
auto dec = make_decl_usingtree(loc, make_expr_string(loc, inst.data[0]));
program_->declarations.push_back(decl{ std::move(dec) });
}
stack_.push(make_expr_animtree(loc));
break;
}
case opcode::OP_GetThisthread:
{
stack_.push(make_expr_thisthread(loc));
break;
}
case opcode::OP_GetBuiltinFunction:
case opcode::OP_GetBuiltinMethod:
{
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_reference(loc, std::move(path), std::move(name)));
break;
}
case opcode::OP_GetLocalFunction:
{
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_reference(loc, std::move(path), std::move(name)));
break;
}
case opcode::OP_GetFarFunction:
{
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
stack_.push(make_expr_reference(loc, std::move(path), std::move(name)));
break;
}
case opcode::OP_CreateLocalVariable:
{
if (in_waittill_)
{
stack_.push(make_asm_create(loc, inst.data[0]));
}
else
{
func_->body->block->list.push_back(stmt{ make_asm_create(loc, inst.data[0]) });
}
break;
}
case opcode::OP_RemoveLocalVariables:
{
func_->body->block->list.push_back(stmt{ make_asm_remove(loc, inst.data[0]) });
break;
}
case opcode::OP_EvalLocalVariableCached0:
{
stack_.push(make_asm_access(loc, "0"));
break;
}
case opcode::OP_EvalLocalVariableCached1:
{
stack_.push(make_asm_access(loc, "1"));
break;
}
case opcode::OP_EvalLocalVariableCached2:
{
stack_.push(make_asm_access(loc, "2"));
break;
}
case opcode::OP_EvalLocalVariableCached3:
{
stack_.push(make_asm_access(loc, "3"));
break;
}
case opcode::OP_EvalLocalVariableCached4:
{
stack_.push(make_asm_access(loc, "4"));
break;
}
case opcode::OP_EvalLocalVariableCached5:
{
stack_.push(make_asm_access(loc, "5"));
break;
}
case opcode::OP_EvalLocalVariableCached:
{
stack_.push(make_asm_access(loc, inst.data[0]));
break;
}
case opcode::OP_EvalLocalArrayCached:
{
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ make_asm_access(loc, inst.data[0]) };
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_EvalArray:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_EvalNewLocalArrayRefCached0:
{
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ make_asm_create(loc, inst.data[0]) };
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_EvalLocalArrayRefCached0:
{
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ make_asm_access(loc, "0") };
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_EvalLocalArrayRefCached:
{
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ make_asm_access(loc, inst.data[0]) };
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_EvalArrayRef:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key)));
break;
}
case opcode::OP_ClearArray:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto key = expr{ std::move(stack_.top()) }; stack_.pop();
loc = key.loc();
auto lvalue = expr{ make_expr_array(loc, std::move(obj), std::move(key)) };
auto rvalue = expr{ make_expr_undefined(loc) };
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_AddArray:
{
auto value = expr{ std::move(stack_.top()) }; stack_.pop();
auto array = std::move(stack_.top()); stack_.pop();
if (array->kind() == node::expr_empty_array)
{
auto args = make_expr_arguments(loc);
args->list.push_back(std::move(value));
stack_.push(make_expr_add_array(array->loc(), std::move(args)));
}
else if (array->kind() == node::expr_add_array)
{
reinterpret_cast<expr_add_array*>(array.get())->args->list.push_back(std::move(value));
stack_.push(std::move(array));
}
else
{
throw decomp_error("unknown add array type (could be an array variable name?)");
}
break;
}
case opcode::OP_PreScriptCall:
{
stack_.push(make_asm_prescriptcall(loc));
break;
}
case opcode::OP_ScriptLocalFunctionCall2:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptLocalFunctionCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptLocalMethodCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptLocalThreadCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptLocalChildThreadCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_ScriptLocalMethodThreadCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptLocalMethodChildThreadCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_ScriptFarFunctionCall2:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptFarFunctionCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptFarMethodCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptFarThreadCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
for (auto i = std::stoul(inst.data[2]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptFarChildThreadCall:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
for (auto i = std::stoul(inst.data[2]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_ScriptFarMethodThreadCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
for (auto i = std::stoul(inst.data[2]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptFarMethodChildThreadCall:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc, inst.data[0]);
auto name = make_expr_identifier(loc, inst.data[1]);
for (auto i = std::stoul(inst.data[2]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_ScriptFunctionCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptMethodCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_prescriptcall)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::normal) }));
break;
}
case opcode::OP_ScriptThreadCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
loc = func.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptChildThreadCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
loc = func.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_ScriptMethodThreadCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::thread) }));
break;
}
case opcode::OP_ScriptMethodChildThreadCallPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::childthread) }));
break;
}
case opcode::OP_CallBuiltinPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
loc = func.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethodPointer:
{
auto args = make_expr_arguments(loc);
auto func = expr{ std::move(stack_.top()) }; stack_.pop();
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin0:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin1:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 1u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin2:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 2u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin3:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 3u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin4:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 4u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin5:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 5u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltin:
{
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod0:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod1:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 1u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod2:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 2u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod3:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 3u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod4:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 4u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod5:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = 5u; i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_CallBuiltinMethod:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto args = make_expr_arguments(loc);
auto path = make_expr_path(loc);
auto name = make_expr_identifier(loc, inst.data[0]);
for (auto i = std::stoul(inst.data[1]); i > 0; i--)
{
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
args->list.push_back(std::move(var));
}
stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) }));
break;
}
case opcode::OP_DecTop:
{
auto exp = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_stmt_call(exp.loc(), std::move(exp)) });
break;
}
case opcode::OP_inc:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_increment(lvalue.loc(), std::move(lvalue), false));
break;
}
case opcode::OP_dec:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_decrement(lvalue.loc(), std::move(lvalue), false));
break;
}
case opcode::OP_bit_or:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_bitwise_or(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_bit_ex_or:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_bitwise_exor(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_bit_and:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_bitwise_and(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_equality:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_equality(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_inequality:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_inequality(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_less:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_less(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_greater:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_greater(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_less_equal:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_less_equal(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_greater_equal:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_greater_equal(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_shift_left:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_shift_left(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_shift_right:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_shift_right(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_plus:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_add(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_minus:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_sub(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_multiply:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_mul(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_divide:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_div(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_mod:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_mod(lvalue.loc(), std::move(lvalue), std::move(rvalue)));
break;
}
case opcode::OP_wait:
{
auto exp = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_stmt_wait(exp.loc(), std::move(exp)) });
break;
}
case opcode::OP_waittillFrameEnd:
{
func_->body->block->list.push_back(stmt{ make_stmt_waittillframeend(loc) });
break;
}
case opcode::OP_waitframe:
{
func_->body->block->list.push_back(stmt{ make_stmt_waitframe(loc) });
break;
}
case opcode::OP_waittill:
{
auto args = make_expr_arguments(loc);
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto event = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_stmt_waittill(event.loc(), std::move(obj), std::move(event), std::move(args)));
in_waittill_ = true;
break;
}
case opcode::OP_waittillmatch:
{
auto args = make_expr_arguments(loc);
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto event = expr{ std::move(stack_.top()) }; stack_.pop();
loc = event.loc();
for (auto i = std::stoul(inst.data[0]); i > 0; i--)
{
auto arg = std::move(stack_.top()); stack_.pop();
loc = arg->loc();
args->list.push_back(std::move(arg));
}
func_->body->block->list.push_back(stmt{ make_stmt_waittillmatch(loc, std::move(obj), std::move(event), std::move(args)) });
break;
}
case opcode::OP_clearparams:
{
if (in_waittill_)
{
auto args = make_expr_arguments(loc);
auto arg = std::move(stack_.top()); stack_.pop();
while (arg->kind() != node::stmt_waittill)
{
args->list.push_back(std::move(arg));
arg = std::move(stack_.top()); stack_.pop();
}
if (arg->kind() == node::stmt_waittill)
{
std::reverse(args->list.begin(), args->list.end());
reinterpret_cast<stmt_waittill*>(arg.get())->args = std::move(args);
in_waittill_ = false;
}
func_->body->block->list.push_back(stmt{ std::move(arg) });
}
break;
}
case opcode::OP_notify:
{
auto args = make_expr_arguments(loc);
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto event = expr{ std::move(stack_.top()) }; stack_.pop();
auto var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
while (var->kind() != node::asm_voidcodepos)
{
args->list.push_back(std::move(var));
var = std::move(stack_.top()); stack_.pop();
loc = var->loc();
}
func_->body->block->list.push_back(stmt{ make_stmt_notify(loc, std::move(obj), std::move(event), std::move(args)) });
break;
}
case opcode::OP_endon:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto event = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_stmt_endon(event.loc(), std::move(obj), std::move(event)) });
break;
}
case opcode::OP_voidCodepos:
{
stack_.push(make_asm_voidcodepos(loc));
break;
}
case opcode::OP_vector:
{
auto x = expr{ std::move(stack_.top()) }; stack_.pop();
auto y = expr{ std::move(stack_.top()) }; stack_.pop();
auto z = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_vector(z.loc(), std::move(x), std::move(y), std::move(z)));
break;
}
case opcode::OP_size:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_size(lvalue.loc(), std::move(lvalue)));
break;
}
case opcode::OP_EvalLevelFieldVariable:
{
auto obj = expr{ make_expr_level(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalAnimFieldVariable:
{
auto obj = expr{ make_expr_anim(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalSelfFieldVariable:
{
auto obj = expr{ make_expr_self(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalFieldVariable:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(obj.loc(), std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalLevelFieldVariableRef:
{
auto obj = expr{ make_expr_level(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalAnimFieldVariableRef:
{
auto obj = expr{ make_expr_anim(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalSelfFieldVariableRef:
{
auto obj = expr{ make_expr_self(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(loc, std::move(obj), std::move(field)));
break;
}
case opcode::OP_EvalFieldVariableRef:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
auto field = make_expr_identifier(loc, inst.data[0]);
stack_.push(make_expr_field(obj.loc(), std::move(obj), std::move(field)));
break;
}
case opcode::OP_ClearFieldVariable:
{
auto obj = expr{ std::move(stack_.top()) }; stack_.pop();
loc = obj.loc();
auto name = make_expr_identifier(loc, inst.data[0]);
auto field = expr{ make_expr_field(loc, std::move(obj), std::move(name)) };
auto undef = expr{ make_expr_undefined(loc) };
auto exp = expr{ make_expr_assign_equal(loc, std::move(field), std::move(undef)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SafeCreateVariableFieldCached:
{
auto name = (ctx_->props() & props::hash) ? inst.data[0] : fmt::format("var_{}", inst.data[0]);
func_->params->list.push_back(make_expr_identifier(loc, name));
break;
}
case opcode::OP_SafeSetWaittillVariableFieldCached:
{
if (stack_.top()->kind() != node::asm_create)
{
stack_.push(make_asm_access(loc, inst.data[0]));
}
break;
}
case opcode::OP_SafeSetVariableFieldCached0:
{
func_->params->list.push_back(make_expr_identifier(loc, "var_0"));
break;
}
case opcode::OP_SafeSetVariableFieldCached:
{
func_->params->list.push_back(make_expr_identifier(loc, "var_" + inst.data[0]));
break;
}
case opcode::OP_EvalLocalVariableRefCached0:
{
stack_.push(make_asm_access(loc, "0"));
break;
}
case opcode::OP_EvalLocalVariableRefCached:
{
stack_.push(make_asm_access(loc, inst.data[0]));
break;
}
case opcode::OP_SetLevelFieldVariableField:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto obj = expr{ make_expr_level(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) };
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SetVariableField:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = lvalue.loc();
if (lvalue == node::expr_increment)
{
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(lvalue)) });
}
else if (lvalue == node::expr_decrement)
{
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(lvalue)) });
}
else
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
}
break;
}
case opcode::OP_SetAnimFieldVariableField:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto obj = expr{ make_expr_anim(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) };
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SetSelfFieldVariableField:
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto obj = expr{ make_expr_self(loc) };
auto field = make_expr_identifier(loc, inst.data[0]);
auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) };
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SetLocalVariableFieldCached0:
{
auto lvalue = expr{ make_asm_access(loc, "0") };
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SetNewLocalVariableFieldCached0:
{
auto lvalue = expr{ make_asm_create(loc, inst.data[0]) };
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
if (func_->body->block->list.size() > 0)
{
std::vector<std::string> vars;
while (func_->body->block->list.back() == node::asm_create)
{
auto& entry = func_->body->block->list.back();
if (loc.begin.line < entry.loc().begin.line)
{
vars.push_back(entry.as_asm_create->index);
func_->body->block->list.pop_back();
continue;
}
break;
}
std::reverse(vars.begin(), vars.end());
lvalue.as_asm_create->vars = vars;
}
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_SetLocalVariableFieldCached:
{
auto lvalue = expr{ make_asm_access(loc, inst.data[0]) };
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = rvalue.loc();
auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) };
func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) });
break;
}
case opcode::OP_ClearLocalVariableFieldCached:
{
func_->body->block->list.push_back(stmt{ make_asm_clear(loc, inst.data[0]) });
break;
}
case opcode::OP_ClearLocalVariableFieldCached0:
{
func_->body->block->list.push_back(stmt{ make_asm_clear(loc, "0") });
break;
}
case opcode::OP_EvalLocalVariableObjectCached:
{
stack_.push(make_asm_access(loc, inst.data[0]));
break;
}
case opcode::OP_BoolNot:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_not(lvalue.loc(), std::move(lvalue)));
break;
}
case opcode::OP_BoolComplement:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_complement(lvalue.loc(), std::move(lvalue)));
break;
}
case opcode::OP_switch:
{
auto test = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_asm_switch(test.loc(), std::move(test), inst.data[0]) });
break;
}
case opcode::OP_endswitch:
{
func_->body->block->list.push_back(stmt{ make_asm_endswitch(loc, inst.data) });
break;
}
case opcode::OP_jump:
{
func_->body->block->list.push_back(stmt{ make_asm_jmp(loc, inst.data[0]) });
if (stack_.size() != 0) tern_labels_.push_back(inst.data[0]);
break;
}
case opcode::OP_jumpback:
{
func_->body->block->list.push_back(stmt{ make_asm_jmp_back(loc, inst.data[0]) });
break;
}
case opcode::OP_JumpOnTrue:
{
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
loc = lvalue.loc();
auto test = expr{ make_expr_not(loc, std::move(lvalue)) };
func_->body->block->list.push_back(stmt{ make_asm_jmp_cond(loc, std::move(test), inst.data[0]) });
break;
}
case opcode::OP_JumpOnFalse:
{
auto test = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.push_back(stmt{ make_asm_jmp_cond(test.loc(), std::move(test), inst.data[0]) });
break;
}
case opcode::OP_JumpOnTrueExpr:
{
auto test = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_asm_jmp_true(test.loc(), std::move(test), inst.data[0]));
expr_labels_.push_back(inst.data[0]);
break;
}
case opcode::OP_JumpOnFalseExpr:
{
auto test = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_asm_jmp_false(test.loc(), std::move(test), inst.data[0]));
expr_labels_.push_back(inst.data[0]);
break;
}
case opcode::OP_FormalParams:
{
auto count = std::stoi(inst.data[0]);
for (auto i = 1; i <= count; i++)
{
auto name = (ctx_->props() & props::hash) ? inst.data[i] : fmt::format("var_{}", inst.data[i]);
func_->params->list.push_back(make_expr_identifier(loc, name));
}
break;
}
case opcode::OP_IsDefined:
{
auto value = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_isdefined(value.loc(), std::move(value)));
break;
}
case opcode::OP_IsTrue:
{
auto value = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_istrue(value.loc(), std::move(value)));
break;
}
case opcode::OP_BoolNotAfterAnd:
{
auto value = expr{ std::move(stack_.top()) }; stack_.pop();
stack_.push(make_expr_not(value.loc(), std::move(value)));
break;
}
case opcode::OP_GetStatHash:
{
stack_.push(make_expr_string(loc, fmt::format("stat_{}", inst.data[0])));
break;
}
case opcode::OP_GetUnkxHash:
{
stack_.push(make_expr_string(loc, fmt::format("hunk_{}", inst.data[0])));
break;
}
case opcode::OP_GetEnumHash:
{
stack_.push(make_expr_string(loc, fmt::format("enum_{}", inst.data[0])));
break;
}
case opcode::OP_GetDvarHash:
{
stack_.push(make_expr_string(loc, fmt::format("dvar_{}", inst.data[0])));
break;
}
case opcode::OP_waittillmatch2:
case opcode::OP_checkclearparams:
case opcode::OP_CastFieldObject:
case opcode::OP_CastBool:
break;
default:
throw decomp_error(fmt::format("unhandled opcode {}", ctx_->opcode_name(inst.opcode)));
}
}
auto decompiler::decompile_expressions(instruction const& inst) -> void
{
auto const itr = labels_.find(inst.index);
if (itr == labels_.end())
return;
for (auto const& exp : expr_labels_)
{
if (exp == itr->second)
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto jump = std::move(stack_.top()); stack_.pop();
auto loc = jump->loc();
if (jump->kind() == node::asm_jmp_true)
{
auto lvalue = std::move(reinterpret_cast<asm_jmp_true*>(jump.get())->test);
stack_.push(make_expr_or(loc, std::move(lvalue), std::move(rvalue)));
}
else if (jump->kind() == node::asm_jmp_false)
{
auto lvalue = std::move(reinterpret_cast<asm_jmp_false*>(jump.get())->test);
stack_.push(make_expr_and(loc, std::move(lvalue), std::move(rvalue)));
}
else
{
throw decomp_error("invalid conditional expression");
}
}
}
for (auto const& tern : tern_labels_)
{
if (tern == itr->second)
{
auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop();
auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop();
func_->body->block->list.pop_back();
auto stm = std::move(func_->body->block->list.back());
func_->body->block->list.pop_back();
if (stm == node::asm_jmp_cond)
{
auto loc = stm.as_cond->loc();
stack_.push(make_expr_ternary(loc, std::move(stm.as_cond->test), std::move(lvalue), std::move(rvalue)));
}
else
{
throw decomp_error("invalid ternary expression");
}
}
}
}
auto decompiler::decompile_statements(stmt_list& stm) -> void
{
decompile_loops(stm);
decompile_switches(stm);
decompile_ifelses(stm);
decompile_aborts(stm);
decompile_tuples(stm);
}
auto decompiler::decompile_loops(stmt_list& stm) -> void
{
if (stm.list.size() == 0)
return;
for (auto i = static_cast<i32>(stm.list.size() - 1); i >= 0; i--)
{
if (stm.list.at(i) == node::asm_jmp_back)
{
auto break_loc = last_location_index(stm, i) ? locs_.end : stm.list.at(i + 1).label();
auto start = find_location_index(stm, stm.list.at(i).as_jump_back->value);
if (i > 0 && stm.list.at(i - 1) == node::asm_jmp_cond)
{
if (i - 1 == static_cast<i32>(start)) // condition belongs to empty loop
{
decompile_while(stm, start, i);
i = static_cast<i32>(stm.list.size());
continue;
}
else if (static_cast<usize>(i) < find_location_index(stm, stm.list.at(i - 1).as_cond->value))
{
decompile_dowhile(stm, i - 1, i);
i = static_cast<i32>(stm.list.size());
continue;
}
}
if (i == static_cast<i32>(start)) // empty inf loop
{
decompile_inf(stm, start, i);
}
else if (stm.list.at(start) != node::asm_jmp_cond) // no condition
{
decompile_inf(stm, start, i);
}
else if (stm.list.at(start).as_cond->value != break_loc) // condition belong to other stmt
{
decompile_inf(stm, start, i);
}
else // condition belong to loop
{
decompile_loop(stm, start, i);
}
i = static_cast<i32>(stm.list.size());
}
}
}
auto decompiler::decompile_switches(stmt_list& stm) -> void
{
for (auto i = 0u; i < stm.list.size(); i++)
{
if (stm.list[i] == node::asm_switch)
{
auto end = find_location_index(stm, stm.list[i].as_asm_switch->value);
decompile_switch(stm, i, end);
}
}
}
auto decompiler::decompile_ifelses(stmt_list& stm) -> void
{
for (auto i = 0u; i < stm.list.size(); i++)
{
auto const& entry = stm.list.at(i);
if (entry == node::asm_jmp_cond)
{
auto j = find_location_index(stm, entry.as_cond->value) - 1;
auto last_loc = locs_.end;
if (stm.list.at(j) == node::asm_jmp)
{
// if scope is a loop check break, continue
if (stm.list.at(j).as_jump->value == locs_.cnt)
{
// check for if/else or if/continue
if (j - i > 1 && stm.list.at(j - 1) == node::stmt_return)
{
// scope ends with a return, so jump belows to if/else
decompile_ifelse(stm, i, j);
}
else if (j - i > 1 && stm.list.at(j - 1) == node::asm_jmp)
{
if (stm.list.at(j - 1).as_jump->value == locs_.brk)
{
// scope ends with a break, so jump belows to if/else
decompile_ifelse(stm, i, j);
}
else if (stm.list.at(j - 1).as_jump->value == locs_.cnt)
{
// if { break/return } else { continue } at loop scope end
if (j - i > 2 && (stm.list.at(j - 2) == node::asm_jmp || stm.list.at(j - 2) == node::stmt_return))
{
decompile_if(stm, i, j);
}
else
{
// scope ends with a continue, so jump belows to if/else
decompile_ifelse(stm, i, j);
}
}
else
{
// jump belows to if/continue
decompile_if(stm, i, j);
}
}
else
{ // last if/else inside a loop still trigger this :(
decompile_if(stm, i, j);
}
}
else if (stm.list.at(j).as_jump->value == locs_.brk)
{
decompile_if(stm, i, j);
}
else if (stm.list.at(j).as_jump->value == entry.as_cond->value)
{
if (find_location_reference(stm, i + 1, j, entry.as_cond->value))
{
// if scope, have a empty else inside at end
decompile_if(stm, i, j);
}
else
{
decompile_ifelse(stm, i, j); // if scope with empty else
}
}
else
{
decompile_ifelse(stm, i, j);
}
}
else if (stm.list.at(j) == node::stmt_return && stm.list.at(j).as_return->value == node::null)
{
if(entry.as_cond->value != locs_.end)
{
auto ref = stm.list.at(j + 1).label();
if (find_location_reference(stm, i + 1, j, ref))
{
// after return is referenced inside the scope
decompile_if(stm, i, j);
continue;
}
}
if (locs_.brk != "" || locs_.cnt != "")
{
decompile_if(stm, i, j); // inside a loop cant be last
}
else if (j - i == 1)
{
decompile_if(stm, i, j); // only one explicit return
}
else if (stm.list.back() != node::stmt_return)
{
decompile_if(stm, i, j); // scope end is not a last return
}
else if (locs_.last && stm.list.back() != node::stmt_return)
{
decompile_if(stm, i, j); // inside a last scope but is not and inner last
}
else if (find_location_reference(stm, j, stm.list.size(), last_loc))
{
decompile_if(stm, i, j); // reference to func end after the if
}
else if (!locs_.last)
{
decompile_if(stm, i, j); // fake last ifelse
}
else
{
decompile_ifelse_end(stm, i, j); // special case
}
}
else
{
decompile_if(stm, i, j);
}
}
}
}
auto decompiler::decompile_aborts(stmt_list& stm) -> void
{
for (auto i = 0u; i < stm.list.size(); i++)
{
if (stm.list[i] == node::asm_jmp)
{
auto const loc = stm.list[i].loc();
auto const jmp = stm.list[i].as_jump->value;
if (jmp == locs_.brk)
{
stm.list.erase(stm.list.begin() + i);
stm.list.insert(stm.list.begin() + i, stmt{ make_stmt_break(loc) });
}
else if (jmp == locs_.cnt)
{
stm.list.erase(stm.list.begin() + i);
stm.list.insert(stm.list.begin() + i, stmt{ make_stmt_continue(loc) });
}
else
{
std::cout << fmt::format("WARNING: unresolved jump to '{}', maybe incomplete for loop\n", jmp);
}
}
}
}
auto decompiler::decompile_tuples(stmt_list& stm) -> void
{
for (auto i = 1u; i < stm.list.size(); i++)
{
if (stm.list.at(i) == node::asm_clear)
{
auto j = i - 1;
auto found = false, done = false;
while (j >= 0 && stm.list.at(j) == node::stmt_assign)
{
auto const& exp = stm.list.at(j).as_assign->value;
if (exp != node::expr_assign_equal)
break;
if (!done)
{
if (exp.as_assign_equal->rvalue != node::expr_array)
break;
if (exp.as_assign_equal->rvalue.as_array->key != node::expr_integer)
break;
if (exp.as_assign_equal->rvalue.as_array->key.as_integer->value == "0")
done = true;
j--;
}
else
{
if (exp.as_assign_equal->lvalue == node::asm_create || exp.as_assign_equal->lvalue == node::asm_access)
found = true;
break;
}
}
if (found)
{
auto& entry = stm.list.at(j); // temp = expr;
auto tuple = make_expr_tuple(entry.loc());
tuple->temp = std::move(entry.as_assign->value.as_assign_equal->lvalue);
j++;
while (j < i)
{
tuple->list.push_back(std::move(stm.list.at(j).as_assign->value.as_assign_equal->lvalue));
stm.list.erase(stm.list.begin() + j);
i--;
}
stm.list.erase(stm.list.begin() + j); // clear temp array
i--;
entry.as_assign->value.as_assign_equal->lvalue = expr{ std::move(tuple) };
}
}
}
}
auto decompiler::decompile_if(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[begin].as_cond->value;
auto loc = stm.list[begin].loc();
auto test = std::move(stm.list[begin].as_cond->test);
stm.list.erase(stm.list.begin() + begin);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_if(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) });
}
auto decompiler::decompile_ifelse(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[end].label();
auto loc = stm.list[begin].loc();
auto test = std::move(stm.list[begin].as_cond->test);
stm.list.erase(stm.list.begin() + begin);
end--;
auto body_if = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body_if->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body_if);
locs_ = save;
auto end_loc = stm.list[begin].as_jump->value;
stm.list.erase(stm.list.begin() + begin);
end = find_location_index(stm, end_loc);
save = locs_;
locs_.last = false;
locs_.end = end_loc;
auto body_else = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body_else->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body_else);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_ifelse(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }, stmt{ make_stmt_comp(loc, std::move(body_else)) }) });
}
auto decompiler::decompile_ifelse_end(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = true;
locs_.end = stm.list[find_location_index(stm, stm.list[begin].as_cond->value) - 1].label();
auto loc = stm.list[begin].loc();
auto test = std::move(stm.list[begin].as_cond->test);
stm.list.erase(stm.list.begin() + begin);
end--;
auto body_if = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body_if->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
stm.list.erase(stm.list.begin() + begin);
decompile_statements(*body_if);
locs_ = save;
if (begin == stm.list.size())
{
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_if(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }) });
}
else
{
end = stm.list.size() - 1;
save = locs_;
locs_.last = true;
locs_.end = stm.list[end].label();
auto body_else = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body_else->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
stm.list.erase(stm.list.begin() + begin);
decompile_statements(*body_else);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_ifelse(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }, stmt{ make_stmt_comp(loc, std::move(body_else)) }) });
}
}
auto decompiler::decompile_inf(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.brk = last_location_index(stm, end) ? locs_.end : stm.list[end + 1].label();
locs_.end = stm.list[end].label();
locs_.cnt = stm.list[end].label();
auto loc = stm.list[begin].loc();
stm.list.erase(stm.list.begin() + end);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_for(loc, stmt{ make_node(loc) }, expr{ make_node(loc) }, stmt{ make_node(loc) }, stmt{ make_stmt_comp(loc, std::move(body)) }) });
}
auto decompiler::decompile_loop(stmt_list& stm, usize start, usize end) -> void
{
auto const& last = stm.list.at(end - 1);
if (last == node::stmt_assign)
{
if (last.as_assign->value == node::expr_assign_equal)
{
auto& val = last.as_assign->value.as_assign_equal->rvalue;
if (val == node::expr_call && val.as_call->value == node::expr_function)
{
if (utils::string::to_lower(val.as_call->value.as_function->name->value) == "getnextarraykey")
{
auto ref = stm.list.at(start).label();
if (!find_location_reference(stm, 0, start, ref))
{
decompile_foreach(stm, start, end);
return;
}
}
}
}
if (start > 0) // while at func start
{
auto index = 1;
while (stm.list.at(start - index) == node::asm_create)
{
if (start - index > 0)
index++;
else
break;
}
if (stm.list.at(start - index) == node::stmt_assign)
{
auto ref = stm.list.at(end).label();
auto ref2 = stm.list.at(start - index + 1).label();
if (find_location_reference(stm, start, end, ref))
{
// jump is referenced, not post-expr
decompile_while(stm, start, end);
return;
}
else if (find_location_reference(stm, 0, start, ref2))
{
// begin is at condition or localVarCreate, not pre-expr
decompile_while(stm, start, end);
return;
}
else
{
decompile_for(stm, start, end);
return;
}
}
}
}
decompile_while(stm, start, end);
}
auto decompiler::decompile_while(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[end].label();
locs_.cnt = stm.list[end].label();
locs_.brk = stm.list[begin].as_cond->value;
auto loc = stm.list[begin].loc();
auto test = std::move(stm.list[begin].as_cond->test);
end--;
stm.list.erase(stm.list.begin() + begin);
stm.list.erase(stm.list.begin() + end);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_while(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) });
}
auto decompiler::decompile_dowhile(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[begin].label();
locs_.cnt = stm.list[begin].label();
locs_.brk = stm.list[begin].as_cond->value;
auto test = std::move(stm.list[begin].as_cond->test);
begin = find_location_index(stm, stm.list[end].as_jump_back->value);
auto loc = stm.list[begin].loc();
end--;
stm.list.erase(stm.list.begin() + end);
stm.list.erase(stm.list.begin() + end);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_dowhile(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) });
}
auto decompiler::decompile_for(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[end - 1].label();
locs_.cnt = stm.list[end - 1].label();
locs_.brk = stm.list[begin].as_cond->value;
for (begin -= 1; stm.list[begin] == node::asm_create; begin--);
auto loc = stm.list[begin].loc();
auto init = make_stmt_list(loc);
while (stm.list[begin] != node::asm_jmp_cond)
{
init->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
auto test = std::move(stm.list[begin].as_cond->test);
stm.list.erase(stm.list.begin() + begin);
end -= 2 + init->list.size();
auto iter = make_stmt_list(loc);
iter->list.push_back(std::move(stm.list[end]));
stm.list.erase(stm.list.begin() + end);
stm.list.erase(stm.list.begin() + end);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_for(loc, stmt { std::move(init) }, std::move(test), stmt { std::move(iter) }, stmt{ make_stmt_comp(loc, std::move(body)) }) });
}
auto decompiler::decompile_foreach(stmt_list& stm, usize begin, usize end) -> void
{
auto save = locs_;
locs_.last = false;
locs_.end = stm.list[end - 1].label();
locs_.cnt = stm.list[end - 1].label();
locs_.brk = stm.list[begin].as_cond->value;
for (begin -= 1; stm.list[begin] == node::asm_create; begin--);
auto use_index = false;
if ((ctx_->props() & props::foreach) && stm.list[begin] == node::stmt_assign && stm.list[begin].as_assign->value.as_assign->rvalue == node::expr_undefined)
{
use_index = true;
begin--;
}
begin--;
auto loc = stm.list[begin].loc();
auto container = std::move(stm.list[begin].as_assign->value.as_assign->rvalue);
auto array = std::move(stm.list[begin].as_assign->value.as_assign->lvalue);
stm.list.erase(stm.list.begin() + begin);
auto key = std::move(stm.list[begin].as_assign->value.as_assign->lvalue);
stm.list.erase(stm.list.begin() + begin);
auto index = (use_index) ? std::move(stm.list[begin].as_assign->value.as_assign->lvalue): expr{ make_node() };
if (use_index)
{
stm.list.erase(stm.list.begin() + begin);
}
auto init = make_stmt_list(loc);
while (stm.list[begin] != node::asm_jmp_cond)
{
init->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
stm.list.erase(stm.list.begin() + begin);
auto value = std::move(stm.list[begin].as_assign->value.as_assign->lvalue);
stm.list.erase(stm.list.begin() + begin);
if (use_index)
{
stm.list.erase(stm.list.begin() + begin);
}
end -= (use_index ? 7 : 5) + init->list.size();
stm.list.erase(stm.list.begin() + end);
stm.list.erase(stm.list.begin() + end);
auto use_key = true;
if (stm.list.size() > end && stm.list[end] == node::asm_clear)
{
stm.list.erase(stm.list.begin() + end);
}
if (stm.list.size() > end && stm.list[end] == node::asm_clear)
{
stm.list.erase(stm.list.begin() + end);
use_key = false;
}
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
body->list.insert(body->list.begin(), stmt{ std::move(init) });
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_foreach(loc, std::move(container), std::move(value), std::move(index), std::move(array), std::move(key), stmt{ make_stmt_comp(loc, std::move(body)) }, (ctx_->props() & props::foreach) ? use_index : use_key) });
}
auto decompiler::decompile_switch(stmt_list& stm, usize begin, usize end) -> void
{
auto const& data = stm.list[end].as_asm_endswitch->data;
auto const count = std::stoul(data[0]);
if (count)
{
auto type = static_cast<switch_type>(std::stoul(data.back()));
auto index = 1u;
for (auto i = 0u; i < count; i++)
{
if (data[index] == "case")
{
if (ctx_->engine() == engine::iw9)
{
type = static_cast<switch_type>(std::stoul(data[index + 1]));
auto j = find_location_index(stm, data[index + 3]);
auto loc = stm.list[j].loc();
auto exp = (type == switch_type::integer) ? expr{ make_expr_integer(loc, data[index + 2]) } : expr{ make_expr_string(loc, data[index + 2]) };
while (stm.list[j] == node::stmt_case) j++;
stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_case(loc, std::move(exp), make_stmt_list(loc)) });
index += 4;
}
else
{
auto j = find_location_index(stm, data[index + 2]);
auto loc = stm.list[j].loc();
auto exp = (type == switch_type::integer) ? expr{ make_expr_integer(loc, data[index + 1]) } : expr{ make_expr_string(loc, data[index + 1]) };
while (stm.list[j] == node::stmt_case) j++;
stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_case(loc, std::move(exp), make_stmt_list(loc)) });
index += 3;
}
}
else if (data[index] == "default")
{
auto j = find_location_index(stm, data[index + 1]);
auto loc = stm.list[j].loc();
while (stm.list[j] == node::stmt_case) j++;
stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_default(loc, make_stmt_list(loc)) });
index += 2;
}
else
{
decomp_error("malformed endswitch statement");
}
}
end += count;
}
auto save = locs_;
locs_.last = false;
locs_.brk = last_location_index(stm, end) ? locs_.end : stm.list[end + 1].label();
locs_.end = stm.list[begin].as_asm_switch->value;
auto loc = stm.list[begin].loc();
auto test = std::move(stm.list[begin].as_asm_switch->test);
end--;
stm.list.erase(stm.list.begin() + begin);
stm.list.erase(stm.list.begin() + end);
auto body = make_stmt_list(loc);
for (auto i = begin; i < end; i++)
{
body->list.push_back(std::move(stm.list[begin]));
stm.list.erase(stm.list.begin() + begin);
}
decompile_statements(*body);
locs_ = save;
auto temp = stmt{ make_node() };
for (auto i = 0u; i < body->list.size(); )
{
if (body->list[i] == node::stmt_case || body->list[i] == node::stmt_default)
{
if (temp != node::null)
{
body->list.insert(body->list.begin() + i, std::move(temp));
i++;
}
temp = std::move(body->list[i]);
body->list.erase(body->list.begin() + i);
}
else
{
if (temp == node::stmt_case)
{
temp.as_case->body->list.push_back(std::move(body->list[i]));
body->list.erase(body->list.begin() + i);
}
else if (temp == node::stmt_default)
{
temp.as_default->body->list.push_back(std::move(body->list[i]));
body->list.erase(body->list.begin() + i);
}
else
{
decomp_error("missing case or default before stmt inside a switch");
}
}
}
if (temp != node::null)
{
body->list.push_back(std::move(temp));
}
stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_switch(loc, std::move(test), make_stmt_comp(loc, std::move(body))) });
}
auto decompiler::find_location_reference(stmt_list const& stm, usize begin, usize end, std::string const& loc) -> bool
{
for (auto i = begin; i < end; i++)
{
auto const& entry = stm.list.at(i);
if (entry == node::asm_jmp_cond && entry.as_cond->value == loc)
{
return true;
}
else if (entry == node::asm_jmp && entry.as_jump->value == loc)
{
return true;
}
}
return false;
}
auto decompiler::find_location_index(stmt_list const& stm, std::string const& loc) -> usize
{
auto index = 0u;
if (loc == locs_.end)
return stm.list.size();
for (auto const& entry : stm.list)
{
if (entry.label() == loc)
return index;
index++;
}
throw decomp_error(fmt::format("location '{}' not found", loc));
}
auto decompiler::last_location_index(stmt_list const& stm, usize index) -> bool
{
return (index == stm.list.size() - 1) ? true : false;
}
auto decompiler::process_function(decl_function& func) -> void
{
auto scp_body = make_scope();
for (auto const& entry : func.params->list)
{
scp_body->vars.push_back({ entry->value, static_cast<u8>(scp_body->create_count), true });
scp_body->create_count++;
}
process_stmt_comp(*func.body, *scp_body);
}
auto decompiler::process_stmt(stmt& stm, scope& scp) -> void
{
switch (stm.kind())
{
case node::stmt_list:
process_stmt_list(*stm.as_list, scp);
break;
case node::stmt_comp:
process_stmt_comp(*stm.as_comp, scp);
break;
case node::stmt_dev:
process_stmt_dev(*stm.as_dev, scp);
break;
case node::stmt_expr:
process_stmt_expr(*stm.as_expr, scp);
break;
case node::stmt_call:
process_stmt_call(*stm.as_call, scp);
break;
case node::stmt_assign:
process_stmt_assign(*stm.as_assign, scp);
break;
case node::stmt_endon:
process_stmt_endon(*stm.as_endon, scp);
break;
case node::stmt_notify:
process_stmt_notify(*stm.as_notify, scp);
break;
case node::stmt_wait:
process_stmt_wait(*stm.as_wait, scp);
break;
case node::stmt_waittill:
process_stmt_waittill(*stm.as_waittill, scp);
break;
case node::stmt_waittillmatch:
process_stmt_waittillmatch(*stm.as_waittillmatch, scp);
break;
case node::stmt_if:
process_stmt_if(*stm.as_if, scp);
break;
case node::stmt_ifelse:
process_stmt_ifelse(*stm.as_ifelse, scp);
break;
case node::stmt_while:
process_stmt_while(*stm.as_while, scp);
break;
case node::stmt_dowhile:
process_stmt_dowhile(*stm.as_dowhile, scp);
break;
case node::stmt_for:
process_stmt_for(*stm.as_for, scp);
break;
case node::stmt_foreach:
process_stmt_foreach(*stm.as_foreach, scp);
break;
case node::stmt_switch:
process_stmt_switch(*stm.as_switch, scp);
break;
case node::stmt_break:
process_stmt_break(*stm.as_break, scp);
break;
case node::stmt_continue:
process_stmt_continue(*stm.as_continue, scp);
break;
case node::stmt_return:
process_stmt_return(*stm.as_return, scp);
break;
case node::asm_create:
process_stmt_asm_create(*stm.as_asm_create, scp);
break;
case node::asm_remove:
process_stmt_asm_remove(*stm.as_asm_remove, scp);
break;
default:
break;
}
}
auto decompiler::process_stmt_list(stmt_list& stm, scope& scp) -> void
{
for (auto& entry : stm.list)
{
process_stmt(entry, scp);
}
for (auto i = 0u; i < stm.list.size(); )
{
if (stm.list[i] == node::asm_create || stm.list[i] == node::asm_remove)
stm.list.erase(stm.list.begin() + i);
else
i++;
}
}
auto decompiler::process_stmt_comp(stmt_comp& stm, scope& scp) -> void
{
process_stmt_list(*stm.block, scp);
}
auto decompiler::process_stmt_dev(stmt_dev& stm, scope& scp) -> void
{
process_stmt_list(*stm.block, scp);
}
auto decompiler::process_stmt_expr(stmt_expr& stm, scope& scp) -> void
{
switch (stm.value.kind())
{
case node::expr_increment:
process_expr_increment(*stm.value.as_increment, scp);
break;
case node::expr_decrement:
process_expr_decrement(*stm.value.as_decrement, scp);
break;
case node::expr_assign_equal:
case node::expr_assign_add:
case node::expr_assign_sub:
case node::expr_assign_mul:
case node::expr_assign_div:
case node::expr_assign_mod:
case node::expr_assign_shift_left:
case node::expr_assign_shift_right:
case node::expr_assign_bitwise_or:
case node::expr_assign_bitwise_and:
case node::expr_assign_bitwise_exor:
process_expr_assign(stm.value.as_assign, scp);
break;
default:
break;
}
}
auto decompiler::process_stmt_call(stmt_call& stm, scope& scp) -> void
{
switch (stm.value.kind())
{
case node::expr_call:
process_expr_call(*stm.value.as_call, scp);
break;
case node::expr_method:
process_expr_method(*stm.value.as_method, scp);
break;
default:
break;
}
}
auto decompiler::process_stmt_assign(stmt_assign& stm, scope& scp) -> void
{
switch (stm.value.kind())
{
case node::expr_increment:
process_expr_increment(*stm.value.as_increment, scp);
break;
case node::expr_decrement:
process_expr_decrement(*stm.value.as_decrement, scp);
break;
case node::expr_assign_equal:
case node::expr_assign_add:
case node::expr_assign_sub:
case node::expr_assign_mul:
case node::expr_assign_div:
case node::expr_assign_mod:
case node::expr_assign_shift_left:
case node::expr_assign_shift_right:
case node::expr_assign_bitwise_or:
case node::expr_assign_bitwise_and:
case node::expr_assign_bitwise_exor:
process_expr_assign(stm.value.as_assign, scp);
break;
default:
break;
}
}
auto decompiler::process_stmt_endon(stmt_endon& stm, scope& scp) -> void
{
process_expr(stm.event, scp);
process_expr(stm.obj, scp);
}
void decompiler::process_stmt_notify(stmt_notify& stm, scope& scp)
{
process_expr_arguments(*stm.args, scp);
process_expr(stm.event, scp);
process_expr(stm.obj, scp);
}
auto decompiler::process_stmt_wait(stmt_wait& stm, scope& scp) -> void
{
process_expr(stm.time, scp);
}
auto decompiler::process_stmt_waittill(stmt_waittill& stm, scope& scp) -> void
{
process_expr(stm.event, scp);
process_expr(stm.obj, scp);
for (auto& entry : stm.args->list)
{
process_expr(entry, scp);
}
}
auto decompiler::process_stmt_waittillmatch(stmt_waittillmatch& stm, scope& scp) -> void
{
process_expr_arguments(*stm.args, scp);
process_expr(stm.event, scp);
process_expr(stm.obj, scp);
}
auto decompiler::process_stmt_if(stmt_if& stm, scope& scp) -> void
{
auto scp_then = make_scope();
process_expr(stm.test, scp);
scp.transfer_dec(scp_then);
process_stmt(stm.body, *scp_then);
if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.body = std::move(stm.body.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_ifelse(stmt_ifelse& stm, scope& scp) -> void
{
auto childs = std::vector<scope*>{};
auto abort = scope::abort_return;
auto scp_then = make_scope();
auto scp_else = make_scope();
process_expr(stm.test, scp);
scp.transfer_dec(scp_then);
process_stmt(stm.stmt_if, *scp_then);
if (scp_then->abort <= scope::abort_return)
{
abort = scp_then->abort;
if (abort == scope::abort_none)
childs.push_back(scp_then.get());
}
scp.transfer_dec(scp_else);
process_stmt(stm.stmt_else, *scp_else);
if (scp_else->abort <= abort)
{
abort = scp_else->abort;
if (abort == scope::abort_none)
childs.push_back(scp_else.get());
}
if (scp.abort == scope::abort_none)
scp.abort = abort;
scp.append(childs);
if (stm.stmt_if.as_comp->block->list.size() == 1 && !stm.stmt_if.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.stmt_if = std::move(stm.stmt_if.as_comp->block->list.back());
}
if (stm.stmt_else.as_comp->block->list.size() == 1 && !stm.stmt_else.as_comp->block->list[0].as_node->is_special_stmt_noif())
{
stm.stmt_else = std::move(stm.stmt_else.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_while(stmt_while& stm, scope& scp) -> void
{
auto scp_body = make_scope();
process_expr(stm.test, scp);
scp.transfer_dec(scp_body);
process_stmt(stm.body, *scp_body);
if (stm.test == node::null)
scp.append_dec(scp_body);
if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.body = std::move(stm.body.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_dowhile(stmt_dowhile& stm, scope& scp) -> void
{
auto scp_body = make_scope();
process_expr(stm.test, scp);
scp.transfer_dec(scp_body);
process_stmt(stm.body, *scp_body);
if (stm.test == node::null)
scp.append_dec(scp_body);
if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.body = std::move(stm.body.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_for(stmt_for& stm, scope& scp) -> void
{
auto scp_body = make_scope();
process_stmt(stm.init, scp);
if (stm.init == node::stmt_list && stm.init.as_list->list[0] == node::stmt_assign)
{
stm.init = stmt{ make_stmt_expr(stm.init.loc(), std::move(stm.init.as_list->list[0].as_assign->value)) };
}
scp.transfer_dec(scp_body);
process_expr(stm.test, scp);
process_stmt(stm.body, *scp_body);
process_stmt(stm.iter, scp);
if (stm.iter == node::stmt_list && stm.iter.as_list->list[0] == node::stmt_assign)
{
stm.iter = stmt{ make_stmt_expr(stm.iter.loc(), std::move(stm.iter.as_list->list[0].as_assign->value)) };
}
if (stm.test == node::null)
scp.append_dec(scp_body);
if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.body = std::move(stm.body.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_foreach(stmt_foreach& stm, scope& scp) -> void
{
auto scp_body = make_scope();
process_expr(stm.container, scp);
process_expr(stm.array, scp);
process_expr(stm.key, scp);
if ((ctx_->props() & props::foreach) && stm.use_key)
{
process_expr(stm.index, scp);
}
process_stmt(stm.body.as_comp->block->list[0], scp);
stm.body.as_comp->block->list.erase(stm.body.as_comp->block->list.begin());
scp.transfer_dec(scp_body);
process_expr(stm.value, *scp_body);
process_stmt(stm.body, *scp_body);
if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt())
{
stm.body = std::move(stm.body.as_comp->block->list.back());
}
}
auto decompiler::process_stmt_switch(stmt_switch& stm, scope& scp) -> void
{
auto scopes = std::vector<scope::ptr>{};
auto childs = std::vector<scope*>{};
auto has_default = false;
process_expr(stm.test, scp);
for (auto& entry : stm.body->block->list)
{
if (entry == node::stmt_case)
{
auto scp_case = make_scope();
scp.transfer_dec(scp_case);
process_stmt_list(*entry.as_case->body, *scp_case);
if (scp_case->abort == scope::abort_break)
{
childs.push_back(scp_case.get());
scopes.push_back(std::move(scp_case));
}
}
else if (entry == node::stmt_default)
{
has_default = true;
auto scp_case = make_scope();
scp.transfer_dec(scp_case);
process_stmt_list(*entry.as_default->body, *scp_case);
if (scp_case->abort == scope::abort_break)
{
childs.push_back(scp_case.get());
scopes.push_back(std::move(scp_case));
}
}
}
if (has_default)
{
scp.append(childs);
}
}
auto decompiler::process_stmt_break(stmt_break&, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
scp.abort = scope::abort_break;
}
}
auto decompiler::process_stmt_continue(stmt_continue&, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
scp.abort = scope::abort_continue;
}
}
auto decompiler::process_stmt_return(stmt_return& stm, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
scp.abort = scope::abort_return;
}
if (stm.value != node::null)
{
process_expr(stm.value, scp);
}
}
auto decompiler::process_stmt_asm_create(asm_create& stm, scope& scp) -> void
{
auto var = (ctx_->props() & props::hash) ? stm.index : fmt::format("var_{}", stm.index);
scp.vars.push_back({ var, static_cast<u8>(scp.create_count), true });
scp.create_count++;
}
auto decompiler::process_stmt_asm_remove(asm_remove& stm, scope& scp) -> void
{
scp.public_count = static_cast<u32>(scp.vars.size() - std::stoi(stm.index));
}
auto decompiler::process_expr(expr& exp, scope& scp) -> void
{
switch (exp.kind())
{
case node::expr_ternary:
process_expr_ternary(*exp.as_ternary, scp);
break;
case node::expr_and:
process_expr_and(*exp.as_and, scp);
break;
case node::expr_or:
process_expr_or(*exp.as_or, scp);
break;
case node::expr_equality:
case node::expr_inequality:
case node::expr_less:
case node::expr_greater:
case node::expr_less_equal:
case node::expr_greater_equal:
case node::expr_bitwise_or:
case node::expr_bitwise_and:
case node::expr_bitwise_exor:
case node::expr_shift_left:
case node::expr_shift_right:
case node::expr_add:
case node::expr_sub:
case node::expr_mul:
case node::expr_div:
case node::expr_mod:
process_expr_binary(*exp.as_binary, scp);
break;
case node::expr_complement:
process_expr_complement(*exp.as_complement, scp);
break;
case node::expr_not:
process_expr_not(*exp.as_not, scp);
break;
case node::expr_call:
process_expr_call(*exp.as_call, scp);
break;
case node::expr_method:
process_expr_method(*exp.as_method, scp);
break;
case node::expr_isdefined:
process_expr(exp.as_isdefined->value, scp);
break;
case node::expr_istrue:
process_expr(exp.as_istrue->value, scp);
break;
case node::expr_add_array:
process_expr_add_array(*exp.as_add_array, scp);
break;
case node::expr_size:
process_expr_size(*exp.as_size, scp);
break;
case node::expr_tuple:
process_expr_tuple(*exp.as_tuple, scp);
break;
case node::expr_array:
process_expr_array(*exp.as_array, scp);
break;
case node::expr_field:
process_expr_field(*exp.as_field, scp);
break;
case node::expr_vector:
process_expr_vector(*exp.as_vector, scp);
break;
case node::asm_create:
process_expr_asm_create(exp, scp);
break;
case node::asm_access:
process_expr_asm_access(exp, scp);
break;
default:
break;
}
}
auto decompiler::process_expr_assign(expr_assign::ptr& exp, scope& scp) -> void
{
process_expr(exp->rvalue, scp);
process_expr(exp->lvalue, scp);
if (exp->kind() == node::expr_assign_equal)
{
switch (exp->rvalue.kind())
{
case node::expr_bitwise_or:
if (exp->lvalue == exp->rvalue.as_bitwise_or->lvalue)
exp = make_expr_assign_bitwise_or(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_or->rvalue));
break;
case node::expr_bitwise_and:
if (exp->lvalue == exp->rvalue.as_bitwise_and->lvalue)
exp = make_expr_assign_bitwise_and(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_and->rvalue));
break;
case node::expr_bitwise_exor:
if (exp->lvalue == exp->rvalue.as_bitwise_exor->lvalue)
exp = make_expr_assign_bitwise_exor(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_exor->rvalue));
break;
case node::expr_shift_left:
if (exp->lvalue == exp->rvalue.as_shift_left->lvalue)
exp = make_expr_assign_shift_left(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_shift_left->rvalue));
break;
case node::expr_shift_right:
if (exp->lvalue == exp->rvalue.as_shift_right->lvalue)
exp = make_expr_assign_shift_right(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_shift_right->rvalue));
break;
case node::expr_add:
if (exp->lvalue == exp->rvalue.as_add->lvalue)
exp = make_expr_assign_add(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_add->rvalue));
break;
case node::expr_sub:
if (exp->lvalue == exp->rvalue.as_sub->lvalue)
exp = make_expr_assign_sub(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_sub->rvalue));
break;
case node::expr_mul:
if (exp->lvalue == exp->rvalue.as_mul->lvalue)
exp = make_expr_assign_mul(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_mul->rvalue));
break;
case node::expr_div:
if (exp->lvalue == exp->rvalue.as_div->lvalue)
exp = make_expr_assign_div(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_div->rvalue));
break;
case node::expr_mod:
if (exp->lvalue == exp->rvalue.as_mod->lvalue)
exp = make_expr_assign_mod(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_mod->rvalue));
break;
default:
break;
}
}
}
auto decompiler::process_expr_increment(expr_increment& exp, scope& scp) -> void
{
process_expr(exp.lvalue, scp);
}
auto decompiler::process_expr_decrement(expr_decrement& exp, scope& scp) -> void
{
process_expr(exp.lvalue, scp);
}
auto decompiler::process_expr_ternary(expr_ternary& exp, scope& scp) -> void
{
process_expr(exp.test, scp);
process_expr(exp.true_expr, scp);
process_expr(exp.false_expr, scp);
}
auto decompiler::process_expr_binary(expr_binary& exp, scope& scp) -> void
{
process_expr(exp.lvalue, scp);
process_expr(exp.rvalue, scp);
auto prec = exp.lvalue.as_node->precedence();
if (prec && prec < exp.precedence())
{
exp.lvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.lvalue)) };
}
prec = exp.rvalue.as_node->precedence();
if ((prec && prec < exp.precedence()) || (prec == exp.precedence() && exp.kind() == exp.rvalue.as_node->kind()))
{
exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) };
}
}
auto decompiler::process_expr_and(expr_and& exp, scope& scp) -> void
{
process_expr(exp.lvalue, scp);
process_expr(exp.rvalue, scp);
auto prec = exp.lvalue.as_node->precedence();
if (prec && prec < exp.precedence())
{
exp.lvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.lvalue)) };
}
prec = exp.rvalue.as_node->precedence();
if ((prec && prec < exp.precedence()) || (prec == exp.precedence() && exp.kind() == exp.rvalue.kind()))
{
exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) };
}
}
auto decompiler::process_expr_or(expr_or& exp, scope& scp) -> void
{
process_expr(exp.lvalue, scp);
process_expr(exp.rvalue, scp);
}
auto decompiler::process_expr_complement(expr_complement& exp, scope& scp) -> void
{
process_expr(exp.rvalue, scp);
if (exp.rvalue.as_node->is_binary())
{
exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) };
}
}
auto decompiler::process_expr_not(expr_not& exp, scope& scp) -> void
{
process_expr(exp.rvalue, scp);
if (exp.rvalue.as_node->is_binary())
{
exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) };
}
}
auto decompiler::process_expr_call(expr_call& exp, scope& scp) -> void
{
switch (exp.value.kind())
{
case node::expr_pointer:
process_expr_call_pointer(*exp.value.as_pointer, scp);
break;
case node::expr_function:
process_expr_call_function(*exp.value.as_function, scp);
break;
default:
break;
}
}
auto decompiler::process_expr_method(expr_method& exp, scope& scp) -> void
{
switch (exp.value.kind())
{
case node::expr_pointer:
process_expr_method_pointer(*exp.value.as_pointer, exp.obj, scp);
break;
case node::expr_function:
process_expr_method_function(*exp.value.as_function, exp.obj, scp);
break;
default:
break;
}
}
auto decompiler::process_expr_call_pointer(expr_pointer& exp, scope& scp) -> void
{
process_expr_arguments(*exp.args, scp);
process_expr(exp.func, scp);
}
auto decompiler::process_expr_call_function(expr_function& exp, scope& scp) -> void
{
process_expr_arguments(*exp.args, scp);
}
auto decompiler::process_expr_method_pointer(expr_pointer& exp, expr& obj, scope& scp) -> void
{
process_expr_arguments(*exp.args, scp);
process_expr(obj, scp);
process_expr(exp.func, scp);
}
auto decompiler::process_expr_method_function(expr_function& exp, expr& obj, scope& scp) -> void
{
process_expr_arguments(*exp.args, scp);
process_expr(obj, scp);
}
auto decompiler::process_expr_arguments(expr_arguments& exp, scope& scp) -> void
{
for (auto i = exp.list.size(); i > 0; i--)
{
process_expr(exp.list[i - 1], scp);
}
}
auto decompiler::process_expr_add_array(expr_add_array& exp, scope& scp) -> void
{
for (auto& entry : exp.args->list)
{
process_expr(entry, scp);
}
}
auto decompiler::process_expr_size(expr_size& exp, scope& scp) -> void
{
process_expr(exp.obj, scp);
}
auto decompiler::process_expr_tuple(expr_tuple& exp, scope& scp) -> void
{
process_expr(exp.temp, scp);
for (auto& entry : exp.list)
{
process_expr(entry, scp);
}
}
auto decompiler::process_expr_array(expr_array& exp, scope& scp) -> void
{
process_expr(exp.key, scp);
process_expr(exp.obj, scp);
}
auto decompiler::process_expr_field(expr_field& exp, scope& scp) -> void
{
process_expr(exp.obj, scp);
}
auto decompiler::process_expr_vector(expr_vector& exp, scope& scp) -> void
{
process_expr(exp.z, scp);
process_expr(exp.y, scp);
process_expr(exp.x, scp);
}
auto decompiler::process_expr_asm_create(expr& exp, scope& scp) -> void
{
for (auto const& entry : exp.as_asm_create->vars)
{
auto var = (ctx_->props() & props::hash) ? entry : fmt::format("var_{}", entry);
scp.vars.push_back({ var, static_cast<u8>(scp.create_count), true });
scp.create_count++;
}
auto var = (ctx_->props() & props::hash) ? exp.as_asm_create->index : fmt::format("var_{}", exp.as_asm_create->index);
scp.vars.push_back({ var, static_cast<u8>(scp.create_count), true });
scp.create_count++;
exp = expr{ make_expr_identifier(exp.loc(), var) };
}
auto decompiler::process_expr_asm_access(expr& exp, scope& scp) -> void
{
auto const index = std::stoul(exp.as_asm_access->index);
if (scp.vars.size() <= index)
{
std::cout << fmt::format("WARNING: bad local var access\n");
}
else
{
exp = expr{ make_expr_identifier(exp.loc(), scp.vars[scp.vars.size() - 1 - index].name) };
}
}
} // namespace xsk::gsc