From 635c6a0a185e590d041f5fee8297b98ec50b24b4 Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 10:00:42 -0300 Subject: Add GDScript disassembler --- modules/gdscript/gdscript_function.cpp | 521 ++++++++++++++++++++++++++++++++- modules/gdscript/gdscript_function.h | 12 +- 2 files changed, 515 insertions(+), 18 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index a4e37a79f8..aa48a7cdb4 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -34,6 +34,10 @@ #include "gdscript.h" #include "gdscript_functions.h" +#ifdef DEBUG_ENABLED +#include "core/string_builder.h" +#endif + Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const { int address = p_address & ADDR_MASK; @@ -105,9 +109,9 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta #ifdef TOOLS_ENABLED case ADDR_TYPE_NAMED_GLOBAL: { #ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _named_globals_count, nullptr); + ERR_FAIL_INDEX_V(address, _global_names_count, nullptr); #endif - StringName id = _named_globals_ptr[address]; + StringName id = _global_names_ptr[address]; if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) { return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id]; @@ -212,7 +216,6 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&OPCODE_CALL_RETURN, \ &&OPCODE_CALL_ASYNC, \ &&OPCODE_CALL_BUILT_IN, \ - &&OPCODE_CALL_SELF, \ &&OPCODE_CALL_SELF_BASE, \ &&OPCODE_AWAIT, \ &&OPCODE_AWAIT_RESUME, \ @@ -1139,10 +1142,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_CALL_SELF) { - OPCODE_BREAK; - } - OPCODE(OPCODE_CALL_SELF_BASE) { CHECK_SPACE(2); int self_fun = _code_ptr[ip + 1]; @@ -1214,8 +1213,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_AWAIT) { - int ipofs = 2; - CHECK_SPACE(3); + CHECK_SPACE(2); //do the oneshot connect GET_VARIANT_PTR(argobj, 1); @@ -1265,7 +1263,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.stack_size = _stack_size; gdfs->state.self = self; gdfs->state.alloca_size = alloca_size; - gdfs->state.ip = ip + ipofs; + gdfs->state.ip = ip + 2; gdfs->state.line = line; gdfs->state.script = _script; { @@ -1884,3 +1882,506 @@ GDScriptFunctionState::~GDScriptFunctionState() { instances_list.remove_from_list(); } } + +#ifdef DEBUG_ENABLED +static String _get_variant_string(const Variant &p_variant) { + String txt; + if (p_variant.get_type() == Variant::STRING) { + txt = "\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::STRING_NAME) { + txt = "&\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::NODE_PATH) { + txt = "^\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::OBJECT) { + Object *obj = p_variant; + if (!obj) { + txt = "null"; + } else { + GDScriptNativeClass *cls = Object::cast_to(obj); + if (cls) { + txt += cls->get_name(); + txt += " (class)"; + } else { + txt = obj->get_class(); + if (obj->get_script_instance()) { + txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")"; + } + } + } + } else { + txt = p_variant; + } + return txt; +} + +static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) { + int addr = p_address & GDScriptFunction::ADDR_MASK; + + switch (p_address >> GDScriptFunction::ADDR_BITS) { + case GDScriptFunction::ADDR_TYPE_SELF: { + return "self"; + } break; + case GDScriptFunction::ADDR_TYPE_CLASS: { + return "class"; + } break; + case GDScriptFunction::ADDR_TYPE_MEMBER: { + return "member(" + p_script->debug_get_member_by_index(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: { + return "class_const(" + p_function.get_global_name(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: { + return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_STACK: { + return "stack(" + itos(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: { + return "var_stack(" + itos(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_GLOBAL: { + return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: { + return "named_global(" + p_function.get_global_name(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_NIL: { + return "nil"; + } break; + } + + return ""; +} + +void GDScriptFunction::disassemble(const Vector &p_code_lines) const { +#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip])) + + for (int ip = 0; ip < _code_size;) { + StringBuilder text; + int incr = 0; + + text += " "; + text += itos(ip); + text += ": "; + + // This makes the compiler complain if some opcode is unchecked in the switch. + Opcode code = Opcode(_code_ptr[ip]); + + switch (code) { + case OPCODE_OPERATOR: { + int operation = _code_ptr[ip + 1]; + + text += "operator "; + + text += DADDR(4); + text += " = "; + text += DADDR(2); + text += " "; + text += Variant::get_operator_name(Variant::Operator(operation)); + text += " "; + text += DADDR(3); + + incr += 5; + } break; + case OPCODE_EXTENDS_TEST: { + text += "is object "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += " is "; + text += DADDR(2); + + incr += 4; + } break; + case OPCODE_IS_BUILTIN: { + text += "is builtin "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += " is "; + text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2])); + + incr += 4; + } break; + case OPCODE_SET: { + text += "set "; + text += DADDR(1); + text += "["; + text += DADDR(2); + text += "] = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_GET: { + text += "get "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += "["; + text += DADDR(2); + text += "]"; + + incr += 4; + } break; + case OPCODE_SET_NAMED: { + text += "set_named "; + text += DADDR(1); + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 2]]; + text += "\"] = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_GET_NAMED: { + text += "get_named "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 2]]; + text += "\"]"; + + incr += 4; + } break; + case OPCODE_SET_MEMBER: { + text += "set_member "; + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 1]]; + text += "\"] = "; + text += DADDR(2); + + incr += 3; + } break; + case OPCODE_GET_MEMBER: { + text += "get_member "; + text += DADDR(2); + text += " = "; + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 1]]; + text += "\"]"; + + incr += 3; + } break; + case OPCODE_ASSIGN: { + text += "assign "; + text += DADDR(1); + text += " = "; + text += DADDR(2); + + incr += 3; + } break; + case OPCODE_ASSIGN_TRUE: { + text += "assign "; + text += DADDR(1); + text += " = true"; + + incr += 2; + } break; + case OPCODE_ASSIGN_FALSE: { + text += "assign "; + text += DADDR(1); + text += " = false"; + + incr += 2; + } break; + case OPCODE_ASSIGN_TYPED_BUILTIN: { + text += "assign typed builtin ("; + text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]); + text += ") "; + text += DADDR(2); + text += " = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_ASSIGN_TYPED_NATIVE: { + Variant class_name = _constants_ptr[_code_ptr[ip + 1]]; + GDScriptNativeClass *nc = Object::cast_to(class_name.operator Object *()); + + text += "assign typed native ("; + text += nc->get_name().operator String(); + text += ") "; + text += DADDR(2); + text += " = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_ASSIGN_TYPED_SCRIPT: { + Variant script = _constants_ptr[_code_ptr[ip + 1]]; + Script *sc = Object::cast_to