Files
Webcam/include/modules/script_squirrel/squirrel_debugger.cpp

399 lines
9.6 KiB
C++

/* -----------------------------------------------------------------------------
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 <DebuggerVariable *> &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 <String *> 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("<Variable=<Name=\"%s\">\n", v->id.c_str());
switch (v->type)
{
case OT_NULL: s += "<ScriptType=\"Null\">\n"; break;
case OT_TABLE: s += "<ScriptType=\"Table\">\n"; break;
case OT_ARRAY: s += "<ScriptType=\"Array\">\n"; break;
case OT_USERDATA: s += "<ScriptType=\"UserData\">\n"; break;
case OT_CLOSURE: s += "<ScriptType=\"Closure\">\n"; break;
case OT_NATIVECLOSURE: s += "<ScriptType=\"NativeClosure\">\n"; break;
case OT_GENERATOR: s += "<ScriptType=\"Generator\">\n"; break;
case OT_USERPOINTER: s += "<ScriptType=\"UserPointer\">\n"; break;
case OT_THREAD: s += "<ScriptType=\"Thread\">\n"; break;
case OT_FUNCPROTO: s += "<ScriptType=\"FuncProto\">\n"; break;
case OT_CLASS: s += "<ScriptType=\"Class\">\n"; break;
case OT_INSTANCE: s += "<ScriptType=\"Instance\">\n"; break;
case OT_WEAKREF: s += "<ScriptType=\"Weakref\">\n"; break;
case OT_BOOL: s += "<ScriptType=\"Bool\">\n"; break;
case OT_STRING: s += "<ScriptType=\"String\">\n"; break;
case OT_INTEGER: s += "<ScriptType=\"Integer\">\n"; break;
case OT_FLOAT: s += "<ScriptType=\"Float\">\n"; break;
}
// Display type.
s += String::Format("<Type=\"%s\">\n", v->type_string.c_str());
// Value.
if (!v->value.IsEmpty())
s += String::Format("<ValueString=\"%s\">\n", v->value.c_str());
if (v->modified)
s += "<Modified>";
// Members.
if (v->member_list.GetCount())
{
s += "<Members=\n";
ListForeachPtr(DebuggerVariable *, m, v->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();
}