/*************************************************************************/ /* gdscript_disassembler.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #ifdef DEBUG_ENABLED #include "gdscript_function.h" #include "core/string/string_builder.h" #include "gdscript.h" 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] & INSTR_MASK); int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS; switch (code) { case OPCODE_OPERATOR: { int operation = _code_ptr[ip + 4]; text += "operator "; text += DADDR(3); text += " = "; text += DADDR(1); text += " "; text += Variant::get_operator_name(Variant::Operator(operation)); text += " "; text += DADDR(2); incr += 5; } break; case OPCODE_OPERATOR_VALIDATED: { text += "validated operator "; text += DADDR(3); text += " = "; text += DADDR(1); text += " "; text += DADDR(2); 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(2); text += " = "; text += DADDR(1); text += " is "; text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3])); incr += 4; } break; case OPCODE_SET_KEYED: { text += "set keyed "; text += DADDR(1); text += "["; text += DADDR(2); text += "] = "; text += DADDR(3); incr += 4; } break; case OPCODE_SET_KEYED_VALIDATED: { text += "set keyed validated "; text += DADDR(1); text += "["; text += DADDR(2); text += "] = "; text += DADDR(3); incr += 5; } break; case OPCODE_SET_INDEXED_VALIDATED: { text += "set indexed validated "; text += DADDR(1); text += "["; text += DADDR(2); text += "] = "; text += DADDR(3); incr += 5; } break; case OPCODE_GET_KEYED: { text += "get keyed "; text += DADDR(3); text += " = "; text += DADDR(1); text += "["; text += DADDR(2); text += "]"; incr += 4; } break; case OPCODE_GET_KEYED_VALIDATED: { text += "get keyed validated "; text += DADDR(3); text += " = "; text += DADDR(1); text += "["; text += DADDR(2); text += "]"; incr += 5; } break; case OPCODE_GET_INDEXED_VALIDATED: { text += "get indexed validated "; text += DADDR(3); text += " = "; text += DADDR(1); text += "["; text += DADDR(2); text += "]"; incr += 5; } break; case OPCODE_SET_NAMED: { text += "set_named "; text += DADDR(1); text += "[\""; text += _global_names_ptr[_code_ptr[ip + 3]]; text += "\"] = "; text += DADDR(2); incr += 4; } break; case OPCODE_SET_NAMED_VALIDATED: { text += "set_named validated "; text += DADDR(1); text += "[\""; text += ""; text += "\"] = "; text += DADDR(2); incr += 4; } break; case OPCODE_GET_NAMED: { text += "get_named "; text += DADDR(2); text += " = "; text += DADDR(1); text += "[\""; text += _global_names_ptr[_code_ptr[ip + 3]]; text += "\"]"; incr += 4; } break; case OPCODE_GET_NAMED_VALIDATED: { text += "get_named validated "; text += DADDR(2); text += " = "; text += DADDR(1); text += "[\""; text += ""; text += "\"]"; incr += 4; } break; case OPCODE_SET_MEMBER: { text += "set_member "; text += "[\""; text += _global_names_ptr[_code_ptr[ip + 2]]; text += "\"] = "; text += DADDR(1); incr += 3; } break; case OPCODE_GET_MEMBER: { text += "get_member "; text += DADDR(1); text += " = "; text += "[\""; text += _global_names_ptr[_code_ptr[ip + 2]]; 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 + 3]); text += ") "; text += DADDR(1); text += " = "; text += DADDR(2); incr += 4; } break; case OPCODE_ASSIGN_TYPED_NATIVE: { text += "assign typed native ("; text += DADDR(3); text += ") "; text += DADDR(1); text += " = "; text += DADDR(2); incr += 4; } break; case OPCODE_ASSIGN_TYPED_SCRIPT: { Variant script = _constants_ptr[_code_ptr[ip + 3]]; Script *sc = Object::cast_to