/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "script_squirrel/squirrel_debugger.h" #include "script_squirrel/cobject/cobject.h" #include "script_squirrel/cobject/vector_decl.h" #include "math/vector.h" #include "log/log.h" using namespace GS; using namespace GS::Script; //------------------------------------------------------------------------------ String SquirrelDebugger::FormatParameter(DebuggerVariable *var) { switch (var->type) { case OT_STRING: return String::Format("%s=%s", var->id.c_str(), var->v_string.c_str()); case OT_FLOAT: return String::Format("%s=%.4f", var->id.c_str(), var->v_float); case OT_INTEGER: return String::Format("%s=%d", var->id.c_str(), var->v_int); case OT_BOOL: return String::Format("%s=%s", var->id.c_str(), var->v_bool ? "True" : "False"); } return var->id; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static int DebuggerCompareVariable(DebuggerVariable *&v, const char *s) { return !String::Compare(v->id, s); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ DebuggerVariable *SquirrelDebugger::InsertVariable(const char *name, AutoList &debug_var, int level) { // Catch nesting limit. if (level == 3) return NULL; // Try to locate variable in the current watch. DebuggerVariable *var = ListFindEx(debug_var, DebuggerCompareVariable, name); // Retrieve stack variable type. bool check_change = false, has_changed = false; SQObjectType sq_type = sq_gettype(vm, -1); if (var) { // If type has changed, drop the whole member watch structure. if (var->type != (uint)sq_type) var->member_list.Clear(); else check_change = true; } else { var = new DebuggerVariable; if (!var) __ERR__(__LOG_E__ << "Failed to allocate debugger watch structure.\n", NULL); debug_var.Add(var); var->id = name; } // Update the variable. var->referenced = true; var->type = sq_type; String type = "Unknown", value; int limit = 64 / (level + 1); // Increasingly limit array exploration the deeper in the hierarchy we get. switch (sq_type) { case OT_NULL: type = "Null"; break; case OT_TABLE: { type = "Table"; // Check item slot. sq_pushnull(vm); // Iterator. while (SQ_SUCCEEDED(sq_next(vm, -2))) { // Here -1 is the value and -2 is the key. const SQChar *slot_name; if (sq_getstring(vm, -2, &slot_name) == SQ_OK) InsertVariable(slot_name, var->member_list, level + 1); sq_pop(vm, 2); } sq_pop(vm, 1); } break; case OT_ARRAY: { type = "Array"; SQInteger array_size = sq_getsize(vm, -1); value = String::Format("{ Length=%d, [...] }", array_size); // Check item slot. sq_pushnull(vm); // Iterator. while (SQ_SUCCEEDED(sq_next(vm, -2))) { int idx; sq_getinteger(vm, -2, (SQInteger *)&idx); String slot_name = String::Format("[%d]", idx); InsertVariable(slot_name, var->member_list, level + 1); sq_pop(vm, 2); if (--limit == 0) break; } sq_pop(vm, 1); } break; case OT_CLOSURE: type = "Function"; break; case OT_NATIVECLOSURE: type = "C/C++"; break; case OT_OUTER: type = "Outer"; break; case OT_USERDATA: type = "User data"; break; case OT_GENERATOR: type = "Generator"; break; case OT_USERPOINTER: break; case OT_THREAD: type = "Thread"; break; case OT_FUNCPROTO: type = "Prototype"; break; case OT_CLASS: type = "Class definition"; break; case OT_INSTANCE: { type = "Class instance"; Vector4 *v = NULL; CObjectType typetag; if (CObject::GetType(vm, -1, typetag)) { type = CObjectTypeToString(typetag); value = FormatUserObjectParameter(typetag); } else if (SQ_SUCCEEDED(sq_getinstanceup(vm, -1, (SQUserPointer*)&v, (SQUserPointer)&__Vector_decl))) { type = "Vector"; value = String::Format("{%.2f, %.2f, %.2f, %.2f}", v->x, v->y, v->z, v->w); } else { // Trying iterating over instance members. AutoList member_list; sq_getclass(vm, -1); sq_pushnull(vm); // Iterator. while (SQ_SUCCEEDED(sq_next(vm, -2))) { switch (sq_gettype(vm, -1)) { case OT_CLOSURE: case OT_NATIVECLOSURE: break; default: { const SQChar *slot_name; if (sq_getstring(vm, -2, &slot_name) == SQ_OK) member_list.Add(new String(slot_name)); } break; } sq_pop(vm, 2); } sq_pop(vm, 2); // Get actual instance values. ListForeachPtr(String *, m, member_list) { sq_pushstring(vm, m->c_str(), m->Len()); if (sq_get(vm, -2) == SQ_OK) InsertVariable(m->c_str(), var->member_list, level + 1); sq_pop(vm, 1); } } } break; case OT_WEAKREF: type = "Weakref"; break; case OT_BOOL: { type = "Bool"; SQBool b; sq_getbool(vm, -1, &b); value = b ? "True" : "False"; has_changed = check_change ? var->v_bool != asbool(b) : false; var->v_bool = b ? true : false; } break; case OT_STRING: { type = "String"; const SQChar *s; sq_getstring(vm, -1, &s); value = String::Format("%s", s); has_changed = check_change ? var->v_string != s : false; var->v_string = s; } break; case OT_INTEGER: { type = "Integer"; SQInteger i; sq_getinteger(vm, -1, &i); value = String::Format("%d", i); has_changed = check_change ? var->v_int != i : false; var->v_int = (int)i; } break; case OT_FLOAT: { type = "Float"; SQFloat f; sq_getfloat(vm, -1, &f); value = String::Format("%.4f", f); has_changed = check_change ? var->v_float != f : false; var->v_float = f; } break; } // Value for item with sub-items. if (var->member_list.GetCount() && value.IsEmpty()) { value = "{ "; int max = 4; ListForeachPtr(DebuggerVariable *, v, var->member_list) { value += max > 0 ? FormatParameter(v).c_str() : "..."; value += (iterator.Next() == NULL) || (max == 0) ? " }" : ", "; if (!max--) break; } } var->type_string = type; var->modified = has_changed; var->value = value; return var; } void SquirrelDebugger::RefreshStackFrameLocalsCache() { // Mark all variables as unreferenced. DereferenceVarTree(local_var_tree); // Update local variables. const char *var_name; for (int n = 0; (var_name = sq_getlocal(vm, debug_stack_frame, n)) != NULL; ++n) InsertVariable(var_name, local_var_tree, 0); // Drop unreferenced variables. ListForeachPtr(DebuggerVariable *, v, local_var_tree) if (!v->referenced) local_var_tree.Remove(v); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void SquirrelDebugger::VariableToMetatagString(DebuggerVariable *v, String &s) { s += String::Format("\n", v->id.c_str()); switch (v->type) { case OT_NULL: s += "\n"; break; case OT_TABLE: s += "\n"; break; case OT_ARRAY: s += "\n"; break; case OT_USERDATA: s += "\n"; break; case OT_CLOSURE: s += "\n"; break; case OT_NATIVECLOSURE: s += "\n"; break; case OT_GENERATOR: s += "\n"; break; case OT_USERPOINTER: s += "\n"; break; case OT_THREAD: s += "\n"; break; case OT_FUNCPROTO: s += "\n"; break; case OT_CLASS: s += "\n"; break; case OT_INSTANCE: s += "\n"; break; case OT_WEAKREF: s += "\n"; break; case OT_BOOL: s += "\n"; break; case OT_STRING: s += "\n"; break; case OT_INTEGER: s += "\n"; break; case OT_FLOAT: s += "\n"; break; } // Display type. s += String::Format("\n", v->type_string.c_str()); // Value. if (!v->value.IsEmpty()) s += String::Format("\n", v->value.c_str()); if (v->modified) s += ""; // Members. if (v->member_list.GetCount()) { s += "member_list) VariableToMetatagString(m, s); s += ">\n"; } s += ">\n"; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void SquirrelDebugger::GetStackFrameSource(const char *&source, int &line) { SQStackInfos si; if (SQ_SUCCEEDED(sq_stackinfos(vm, debug_stack_frame, &si))) { source = (const char *)si.source; line = si.line; } else { source = NULL; line = -1; } } int SquirrelDebugger::GetStackFrameIndex() { int depth = 0; SQStackInfos si; for (int c = 0; SQ_SUCCEEDED(sq_stackinfos(vm, c, &si)); ++c) if (String(si.source) != "NATIVE") // Skip native C frame. { depth = c; break; } return depth; } int SquirrelDebugger::GetCallstackDepth() { int depth = 0; SQStackInfos si; while (SQ_SUCCEEDED(sq_stackinfos(vm, depth, &si))) depth++; return depth; } //------------------------------------------------------------------------------ SquirrelDebugger::SquirrelDebugger(SquirrelVM &svm) { vm = svm.VM(); }