mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Ide/Debuggers/Gdb_MI2 : many enhancements and speedups - still experimental
git-svn-id: svn://ultimatepp.org/upp/trunk@6910 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
91a665d8d4
commit
fcbd6ec6eb
8 changed files with 896 additions and 140 deletions
|
|
@ -11,6 +11,8 @@ file
|
|||
Gdb.lay,
|
||||
MIValue.h,
|
||||
MIValue.cpp,
|
||||
TypeSimplify.cpp,
|
||||
VarItem.cpp,
|
||||
Terminal.cpp,
|
||||
Disas.cpp,
|
||||
Dbg.cpp,
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ bool Gdb_MI2::RunTo()
|
|||
Run();
|
||||
if(df)
|
||||
disas.SetFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +155,7 @@ void Gdb_MI2::Run()
|
|||
ReadGdb(false);
|
||||
}
|
||||
Unlock();
|
||||
|
||||
if(stopped)
|
||||
CheckStopReason();
|
||||
|
||||
|
|
@ -167,6 +169,8 @@ void Gdb_MI2::AsyncBrk()
|
|||
// send an interrupt command to all running threads
|
||||
StopAllThreads();
|
||||
|
||||
dataSynced = false;
|
||||
|
||||
// gdb usually returns to command prompt instantly, BEFORE
|
||||
// giving out stop reason, which we need. So, we wait some
|
||||
// milliseconds and re-read (non blocking) GDB output to get it
|
||||
|
|
@ -196,18 +200,33 @@ bool Gdb_MI2::IsFinished()
|
|||
|
||||
bool Gdb_MI2::Tip(const String& exp, CodeEditor::MouseTip& mt)
|
||||
{
|
||||
/*
|
||||
// quick exit
|
||||
if(exp.IsEmpty() || !dbg)
|
||||
return false;
|
||||
|
||||
int idx = localVarNames.Find(exp);
|
||||
String nam, val;
|
||||
if(idx < 0)
|
||||
return false;
|
||||
{
|
||||
idx = thisNames.Find(exp);
|
||||
if(idx < 0)
|
||||
return false;
|
||||
nam = thisFullNames[idx];
|
||||
val = thisValues[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
nam = exp;
|
||||
val = localVarValues[idx];
|
||||
}
|
||||
|
||||
mt.display = &StdDisplay();
|
||||
mt.value = exp + "=" + localVarValues[idx];
|
||||
mt.value = nam + "=" + val;
|
||||
mt.sz = mt.display->GetStdSize(String(mt.value) + "X");
|
||||
return true;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -249,7 +268,6 @@ Gdb_MI2::Gdb_MI2()
|
|||
autos.NoHeader();
|
||||
autos.AddColumn("", 1);
|
||||
autos.AddColumn("", 10);
|
||||
autos.AddColumn("", 3);
|
||||
autos.WhenLeftDouble = THISBACK1(onExploreExpr, &autos);
|
||||
autos.EvenRowColor();
|
||||
autos.OddRowColor();
|
||||
|
|
@ -257,15 +275,20 @@ Gdb_MI2::Gdb_MI2()
|
|||
locals.NoHeader();
|
||||
locals.AddColumn("", 1);
|
||||
locals.AddColumn("", 10);
|
||||
locals.AddColumn("", 3);
|
||||
locals.WhenLeftDouble = THISBACK1(onExploreExpr, &locals);
|
||||
locals.EvenRowColor();
|
||||
locals.OddRowColor();
|
||||
|
||||
members.NoHeader();
|
||||
members.AddColumn("", 3);
|
||||
members.AddColumn("", 10);
|
||||
members.WhenLeftDouble = THISBACK1(onExploreExpr, &members);
|
||||
members.EvenRowColor();
|
||||
members.OddRowColor();
|
||||
|
||||
watches.NoHeader();
|
||||
watches.AddColumn("", 1).Edit(watchedit);
|
||||
watches.AddColumn("", 10);
|
||||
watches.AddColumn("", 3);
|
||||
watches.Inserting().Removing();
|
||||
watches.WhenLeftDouble = THISBACK1(onExploreExpr, &watches);
|
||||
watches.EvenRowColor();
|
||||
|
|
@ -293,6 +316,7 @@ Gdb_MI2::Gdb_MI2()
|
|||
Add(tab.SizePos());
|
||||
tab.Add(autos.SizePos(), "Autos");
|
||||
tab.Add(locals.SizePos(), t_("Locals"));
|
||||
tab.Add(members.SizePos(), t_("This"));
|
||||
tab.Add(watches.SizePos(), t_("Watches"));
|
||||
tab.Add(explorerPane.SizePos(), t_("Explorer"));
|
||||
|
||||
|
|
@ -323,6 +347,7 @@ Gdb_MI2::Gdb_MI2()
|
|||
stopped = false;
|
||||
|
||||
periodic.Set(-50, THISBACK(Periodic));
|
||||
dataSynced = false;
|
||||
}
|
||||
|
||||
Gdb_MI2::~Gdb_MI2()
|
||||
|
|
@ -816,11 +841,15 @@ void Gdb_MI2::StopAllThreads(void)
|
|||
// reselect current thread as it was before stopping all others
|
||||
if(current != "")
|
||||
MICmd("thread-select " + current);
|
||||
|
||||
}
|
||||
|
||||
// check for stop reason
|
||||
void Gdb_MI2::CheckStopReason(void)
|
||||
{
|
||||
// data must be synced on stop...
|
||||
dataSynced = false;
|
||||
|
||||
// we need to store stop reason BEFORE interrupting all other
|
||||
// threads, otherwise it'll be lost
|
||||
MIValue stReason = stopReason;
|
||||
|
|
@ -1079,42 +1108,146 @@ void Gdb_MI2::UpdateLocalVars(void)
|
|||
}
|
||||
}
|
||||
|
||||
// deeply explore a value
|
||||
// taking its members and skipping types
|
||||
// WARNING, it APPENDS results to given arrays
|
||||
void Gdb_MI2::ExploreValueDeep(String baseName, MIValue const &val, Vector<String> &fullNames, Vector<String> &values) const
|
||||
{
|
||||
// if value is a string, just append baseName and value to result
|
||||
if(val.IsString())
|
||||
{
|
||||
fullNames.Add(baseName);
|
||||
values.Add(val.ToString());
|
||||
}
|
||||
// otherwise, if value is a tuple, recursively add all subvalues
|
||||
else if(val.IsTuple())
|
||||
{
|
||||
for(int iVal = 0; iVal < val.GetCount(); iVal++)
|
||||
{
|
||||
String name = val.GetKey(iVal);
|
||||
if(!name.StartsWith("<"))
|
||||
name = baseName + '.' + name;
|
||||
else
|
||||
name = baseName;
|
||||
ExploreValueDeep(name, val.Get(iVal), fullNames, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update 'this' inspector data
|
||||
void Gdb_MI2::UpdateThis(void)
|
||||
{
|
||||
/*
|
||||
MIValue thisVal = MICmd("data-evaluate-expression *this");
|
||||
RLOG(thisVal.Dump());
|
||||
thisNames.Clear();
|
||||
thisExpressions.Clear();
|
||||
thisValues.Clear();
|
||||
|
||||
VarItem v = EvalGdb("*this");
|
||||
v.shortExpression = ".";
|
||||
|
||||
thisNames.Add(v.shortExpression);
|
||||
thisExpressions.Add(v.evaluableExpression);
|
||||
thisValues.Add(v.value);
|
||||
|
||||
Vector<VarItem> vv;
|
||||
if(v.kind == VarItem::SIMPLE)
|
||||
vv = GetChildren(v);
|
||||
else
|
||||
vv = GetChildren(v, 0, 10);
|
||||
|
||||
for(int iChild = 0; iChild < vv.GetCount(); iChild++)
|
||||
{
|
||||
VarItem &v = vv[iChild];
|
||||
thisNames.Add(v.shortExpression);
|
||||
thisExpressions.Add(v.evaluableExpression);
|
||||
thisValues.Add(v.value);
|
||||
}
|
||||
*/
|
||||
|
||||
thisNames.Clear();
|
||||
thisExpressions.Clear();
|
||||
thisValues.Clear();
|
||||
|
||||
Vector<String>names, values;
|
||||
|
||||
MIValue thisVal = MICmd("data-evaluate-expression *this");
|
||||
|
||||
if(!thisVal.IsTuple())
|
||||
return;
|
||||
if(thisVal.Find("value") >= 0)
|
||||
{
|
||||
// weird but the value is quoted, so must be parsed again...
|
||||
MIValue const &tup = thisVal.Get("value");
|
||||
if(!tup.IsString())
|
||||
return;
|
||||
MIValue s(tup.ToString());
|
||||
s.PackNames();
|
||||
TypeSimplify(s);
|
||||
ExploreValueDeep("", s, names, values);
|
||||
}
|
||||
else if(thisVal.Find("variables") >= 0)
|
||||
{
|
||||
MIValue &arr = thisVal.Get("variables");
|
||||
|
||||
if(!arr.IsArray())
|
||||
return;
|
||||
for(int iVal = 0; iVal < arr.GetCount(); iVal++)
|
||||
{
|
||||
MIValue &val = arr[iVal];
|
||||
if(!val.IsTuple() || val.Find("name") < 0 || val.Find("value") < 0)
|
||||
continue;
|
||||
MIValue &s = val.Get("value");
|
||||
s.PackNames();
|
||||
TypeSimplify(s);
|
||||
val.PackNames();
|
||||
ExploreValueDeep("." + val.Get("name").ToString(), s, thisExpressions, thisValues);
|
||||
}
|
||||
}
|
||||
|
||||
// build short names from full names
|
||||
for(int iName = 0; iName < thisExpressions.GetCount(); iName++)
|
||||
{
|
||||
String const &nam = thisExpressions[iName];
|
||||
String shortName;
|
||||
int pos = nam.ReverseFind('.');
|
||||
if(pos < 0)
|
||||
shortName = nam;
|
||||
else
|
||||
shortName = nam.Mid(pos + 1);
|
||||
thisNames.Add(shortName);
|
||||
}
|
||||
|
||||
// here the 'this' object is dumped inside the 2 vectors 'names' and 'values'
|
||||
// we need to do some cleanup, removing empty values, class names and so on
|
||||
// we shall also construct the 'thisNames' vector containing just the last name path
|
||||
// used for tooltip
|
||||
for(int iName = 0; iName < names.GetCount(); iName++)
|
||||
{
|
||||
String const &name = names[iName];
|
||||
if(name.IsEmpty())
|
||||
continue;
|
||||
if(name == "<No data fields>")
|
||||
continue;
|
||||
if(name.StartsWith("_vptr."))
|
||||
continue;
|
||||
|
||||
String const &val = values[iName];
|
||||
if(val.IsEmpty())
|
||||
continue;
|
||||
|
||||
String shortName;
|
||||
int dotPos = name.ReverseFind('.');
|
||||
if(dotPos < 0)
|
||||
shortName = name;
|
||||
else
|
||||
shortName = name.Mid(dotPos + 1);
|
||||
|
||||
thisNames.Add(shortName);
|
||||
thisExpressions.Add(name);
|
||||
thisValues.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
// sync watches treectrl
|
||||
void Gdb_MI2::SyncLocals()
|
||||
{
|
||||
VectorMap<String, String> prev = DataMap2(locals);
|
||||
locals.Clear();
|
||||
|
||||
for(int iLoc = 0; iLoc < localVarNames.GetCount(); iLoc++)
|
||||
locals.Add(localVarNames[iLoc], localVarValues[iLoc]);
|
||||
MarkChanged2(prev, locals);
|
||||
}
|
||||
|
||||
// sync watches treectrl
|
||||
void Gdb_MI2::SyncWatches()
|
||||
{
|
||||
// re-fill the control
|
||||
VectorMap<String, String> prev = DataMap2(watches);
|
||||
for(int i = 0; i < watches.GetCount(); i++)
|
||||
{
|
||||
MIValue res = MICmd("data-evaluate-expression \"" + watches.Get(i, 0).ToString() + "\"");
|
||||
if(res.IsError() || res.IsEmpty())
|
||||
watches.Set(i, 2, t_("<can't evaluate expression>"));
|
||||
else
|
||||
watches.Set(i, 2, res["value"].ToString());
|
||||
}
|
||||
MarkChanged2(prev, watches);
|
||||
}
|
||||
|
||||
// sync auto vars treectrl
|
||||
void Gdb_MI2::SyncAutos()
|
||||
{
|
||||
|
|
@ -1149,9 +1282,49 @@ void Gdb_MI2::SyncAutos()
|
|||
MarkChanged2(prev, autos);
|
||||
}
|
||||
|
||||
// sync watches treectrl
|
||||
void Gdb_MI2::SyncLocals()
|
||||
{
|
||||
VectorMap<String, String> prev = DataMap2(locals);
|
||||
locals.Clear();
|
||||
|
||||
for(int iLoc = 0; iLoc < localVarNames.GetCount(); iLoc++)
|
||||
locals.Add(localVarNames[iLoc], localVarValues[iLoc]);
|
||||
MarkChanged2(prev, locals);
|
||||
}
|
||||
|
||||
void Gdb_MI2::SyncMembers(void)
|
||||
{
|
||||
VectorMap<String, String> prev = DataMap2(members);
|
||||
members.Clear();
|
||||
|
||||
for(int iMem = 0; iMem < thisNames.GetCount(); iMem++)
|
||||
members.Add(thisExpressions[iMem], thisValues[iMem]);
|
||||
MarkChanged2(prev, members);
|
||||
}
|
||||
|
||||
// sync watches treectrl
|
||||
void Gdb_MI2::SyncWatches()
|
||||
{
|
||||
// re-fill the control
|
||||
VectorMap<String, String> prev = DataMap2(watches);
|
||||
for(int i = 0; i < watches.GetCount(); i++)
|
||||
{
|
||||
MIValue res = MICmd("data-evaluate-expression \"" + watches.Get(i, 0).ToString() + "\"");
|
||||
if(res.IsError() || res.IsEmpty())
|
||||
watches.Set(i, 2, t_("<can't evaluate expression>"));
|
||||
else
|
||||
watches.Set(i, 2, res["value"].ToString());
|
||||
}
|
||||
MarkChanged2(prev, watches);
|
||||
}
|
||||
|
||||
// sync data tabs, depending on which tab is shown
|
||||
void Gdb_MI2::SyncData()
|
||||
{
|
||||
if(dataSynced)
|
||||
return;
|
||||
|
||||
// updates stored local variables
|
||||
// those are needed for Autos, Locals and Tooltips
|
||||
UpdateLocalVars();
|
||||
|
|
@ -1160,20 +1333,12 @@ void Gdb_MI2::SyncData()
|
|||
// also used for tooltips and 'this' pane page
|
||||
UpdateThis();
|
||||
|
||||
switch(tab.Get())
|
||||
{
|
||||
case 0:
|
||||
SyncAutos();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SyncLocals();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SyncWatches();
|
||||
break;
|
||||
}
|
||||
SyncAutos();
|
||||
SyncLocals();
|
||||
SyncMembers();
|
||||
SyncWatches();
|
||||
|
||||
dataSynced = true;
|
||||
}
|
||||
|
||||
// watches arrayctrl key handling
|
||||
|
|
@ -1486,7 +1651,7 @@ void Gdb_MI2::onExploreExpr(ArrayCtrl *what)
|
|||
doExplore(expr, "", false, true);
|
||||
|
||||
// activate explorer tab
|
||||
tab.Set(3);
|
||||
tab.Set(4);
|
||||
}
|
||||
|
||||
void Gdb_MI2::onExplorerChild()
|
||||
|
|
@ -1590,8 +1755,8 @@ bool Gdb_MI2::Create(One<Host> _host, const String& exefile, const String& cmdli
|
|||
MICmd("gdb-set args " + cmdline);
|
||||
|
||||
// enable pretty printing
|
||||
SendPrettyPrinters();
|
||||
MICmd("enable-pretty-printing");
|
||||
// SendPrettyPrinters();
|
||||
// MICmd("enable-pretty-printing");
|
||||
|
||||
firstRun = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,64 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
{
|
||||
private:
|
||||
|
||||
// item for a GDB variable
|
||||
struct VarItem : Moveable<VarItem>
|
||||
{
|
||||
typedef enum { SIMPLE, ARRAY, MAP } VarKind;
|
||||
|
||||
// error/empty state
|
||||
bool error;
|
||||
|
||||
// gdb internal variable name
|
||||
String varName;
|
||||
|
||||
// short name
|
||||
String shortExpression;
|
||||
|
||||
// evaluable expression
|
||||
String evaluableExpression;
|
||||
|
||||
// type
|
||||
String type;
|
||||
|
||||
// kind
|
||||
int kind;
|
||||
|
||||
// value of expression for non-sequence types
|
||||
String value;
|
||||
|
||||
// children
|
||||
int numChildren;
|
||||
|
||||
// dynamic varobj--number of children must be queried
|
||||
bool dynamic;
|
||||
|
||||
// check if value contains an error
|
||||
bool IsError(void) const;
|
||||
bool operator!(void) const { return IsError(); }
|
||||
operator bool() { return !IsError(); }
|
||||
|
||||
// constructor
|
||||
VarItem();
|
||||
|
||||
// copy (pick)
|
||||
VarItem(VarItem pick_ &v);
|
||||
VarItem &operator=(pick_ VarItem &v);
|
||||
};
|
||||
|
||||
// evaluate an expression usign gdb variables
|
||||
VarItem EvalGdb(String const &expr);
|
||||
|
||||
// remove GDB variable for item
|
||||
void KillVariable(VarItem &v);
|
||||
|
||||
// fetch variable children
|
||||
Vector<VarItem> GetChildren(MIValue const &val, String const &prePath = "");
|
||||
|
||||
// fetch variable children
|
||||
Vector<VarItem> GetChildren(VarItem &v, int minChild = -1, int maxChild = -1);
|
||||
|
||||
|
||||
// used to post and kill timed callbacks
|
||||
TimeCallback timeCallback;
|
||||
|
||||
|
|
@ -42,12 +100,14 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
DropList frame;
|
||||
DropList threadSelector;
|
||||
TabCtrl tab;
|
||||
ArrayCtrl locals;
|
||||
ArrayCtrl watches;
|
||||
ArrayCtrl autos;
|
||||
|
||||
ArrayCtrl autos;
|
||||
ArrayCtrl locals;
|
||||
ArrayCtrl members;
|
||||
ArrayCtrl watches;
|
||||
ArrayCtrl explorer;
|
||||
|
||||
// explorer stuffs -- just starting
|
||||
ArrayCtrl explorer;
|
||||
EditString explorerExprEdit;
|
||||
Button explorerBackBtn, explorerForwardBtn;
|
||||
StaticRect explorerPane;
|
||||
|
|
@ -111,7 +171,7 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
|
||||
// 'this' variable inspection data
|
||||
Index<String>thisNames;
|
||||
Vector<String>thisFullNames;
|
||||
Vector<String>thisExpressions;
|
||||
Vector<String>thisValues;
|
||||
|
||||
// stored autos expressions, values and types
|
||||
|
|
@ -120,7 +180,18 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
// update local variables on demand
|
||||
void UpdateLocalVars(void);
|
||||
|
||||
// deeply explore a value
|
||||
// taking its members and skipping types
|
||||
// WARNING, it APPENDS results to given arrays
|
||||
void ExploreValueDeep(String baseName, MIValue const &val, Vector<String> &fullNames, Vector<String> &values) const;
|
||||
|
||||
// known types simplifier
|
||||
// takes a MIValue from '-data-evaluate-expression' command and try
|
||||
// do simplify diplay of known types
|
||||
void TypeSimplify(MIValue &val);
|
||||
|
||||
// update 'this' inspector data
|
||||
void UpdateThisDeep(VarItem &v);
|
||||
void UpdateThis(void);
|
||||
|
||||
// logs frame data on console
|
||||
|
|
@ -147,16 +218,20 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
// sync disassembler pane
|
||||
void SyncDisas(MIValue &fInfo, bool fr);
|
||||
|
||||
// sync local variables pane
|
||||
void SyncLocals(void);
|
||||
|
||||
// sync watches treectrl
|
||||
void SyncWatches();
|
||||
|
||||
// sync auto vars treectrl
|
||||
void SyncAutos();
|
||||
|
||||
// sync local variables pane
|
||||
void SyncLocals(void);
|
||||
|
||||
// sync 'this' members pane
|
||||
void SyncMembers(void);
|
||||
|
||||
// sync watches treectrl
|
||||
void SyncWatches();
|
||||
|
||||
// sync data tabs, depending on which tab is shown
|
||||
bool dataSynced;
|
||||
void SyncData();
|
||||
|
||||
// sync ide display with breakpoint position
|
||||
|
|
|
|||
|
|
@ -13,22 +13,67 @@ static MIValue &ErrorMIValue(String const &msg)
|
|||
return v;
|
||||
}
|
||||
|
||||
bool MIValue::expect(String const &where, char exp, int i, String const &s)
|
||||
{
|
||||
if(s[i] == exp)
|
||||
return true;
|
||||
int start = i - 30;
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
if(!s[i])
|
||||
SetError(Format(where + " : Expected '%c', got end of string at pos %d around '%s'", exp, i, s.Mid(start, 60)));
|
||||
else
|
||||
SetError(Format(where + " : Expected '%c', got '%c' at pos %d in '%s'", exp, s[i], i, s.Mid(start, 60)));
|
||||
return false;
|
||||
}
|
||||
|
||||
int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
|
||||
{
|
||||
name.Clear();
|
||||
val.Clear();
|
||||
while(s[i] && (s[i] != '=') && s[i] != ']' && s[i] != '}')
|
||||
name.Cat(s[i++]);
|
||||
if(s[i] != '=')
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
if(!s[i])
|
||||
{
|
||||
SetError("ParsePair:Unexpected end of string");
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
// is starting wirh '[' or '{' take it as a value with empty name
|
||||
if(s[i] == '{' || s[i] == '[')
|
||||
name = "";
|
||||
else
|
||||
{
|
||||
int aCount = 0;
|
||||
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']') || aCount))
|
||||
{
|
||||
name.Cat(s[i]);
|
||||
if(s[i] == '<')
|
||||
aCount++;
|
||||
else if(s[i] == '>')
|
||||
aCount--;
|
||||
i++;
|
||||
|
||||
// skip blanks if not inside <>
|
||||
if(!aCount)
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
}
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
|
||||
if(s[i] != '=')
|
||||
return i;
|
||||
i++;
|
||||
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
}
|
||||
switch(s[i])
|
||||
{
|
||||
case '"':
|
||||
{
|
||||
i = val.ParseString(s, i);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '[':
|
||||
|
|
@ -40,8 +85,10 @@ int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
|
|||
break;
|
||||
|
||||
default:
|
||||
NEVER();
|
||||
i = val.ParseUnquotedString(s, i);
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
@ -51,27 +98,26 @@ int MIValue::ParseTuple(String const &s, int i)
|
|||
type = MITuple;
|
||||
|
||||
// drop opening delimiter
|
||||
if(s[i] != '{')
|
||||
{
|
||||
SetError(Format("Expected '{' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseTuple", '{', i, s))
|
||||
return s.GetCount();
|
||||
}
|
||||
i++;
|
||||
while(s[i] && s[i] != '}')
|
||||
{
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
String name;
|
||||
MIValue val;
|
||||
i = ParsePair(name, val, s, i);
|
||||
tuple.Add(name, val);
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
if(s[i] == '}')
|
||||
break;
|
||||
if(s[i] != ',')
|
||||
{
|
||||
SetError(Format("Expected ',' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseTuple", ',', i, s))
|
||||
return s.GetCount();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
|
|
@ -81,14 +127,15 @@ int MIValue::ParseArray(String const &s, int i)
|
|||
type = MIArray;
|
||||
|
||||
// drop opening delimiter
|
||||
if(s[i] != '[')
|
||||
{
|
||||
SetError(Format("Expected '[' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseArray", '[', i, s))
|
||||
return s.GetCount();
|
||||
}
|
||||
i++;
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
while(s[i] && s[i] != ']')
|
||||
{
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
String name;
|
||||
MIValue val;
|
||||
if(s[i] == '[')
|
||||
|
|
@ -97,16 +144,17 @@ int MIValue::ParseArray(String const &s, int i)
|
|||
i = val.ParseTuple(s, i);
|
||||
else if(s[i] == '"')
|
||||
i = val.ParseString(s, i);
|
||||
else if(s[i] == '<')
|
||||
i = val.ParseAngle(s, i);
|
||||
else
|
||||
i = ParsePair(name, val, s, i);
|
||||
array.Add(val);
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
if(s[i] == ']')
|
||||
break;
|
||||
if(s[i] != ',')
|
||||
{
|
||||
SetError(Format("Expected ',' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseArray", ',', i, s))
|
||||
return s.GetCount();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i + 1;
|
||||
|
|
@ -118,11 +166,8 @@ int MIValue::ParseString(String const &s, int i)
|
|||
type = MIString;
|
||||
|
||||
char c;
|
||||
if(s[i] != '"')
|
||||
{
|
||||
SetError(Format("Expected '\"' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseString", '"', i, s))
|
||||
return s.GetCount();
|
||||
}
|
||||
i++;
|
||||
while( (c = s[i++]) != 0)
|
||||
{
|
||||
|
|
@ -134,11 +179,83 @@ int MIValue::ParseString(String const &s, int i)
|
|||
else
|
||||
string.Cat(c);
|
||||
}
|
||||
if(c != '"')
|
||||
{
|
||||
SetError(Format("Expected '\"' at pos %d in '%s'", i, s));
|
||||
if(!expect("ParseString", '"', i-1, s))
|
||||
return s.GetCount();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int MIValue::ParseAngle(String const &s, int i)
|
||||
{
|
||||
Clear();
|
||||
type = MIString;
|
||||
int aCount = 0;
|
||||
|
||||
char c;
|
||||
if(!expect("ParseAngle", '<', i, s))
|
||||
return s.GetCount();
|
||||
string = "<";
|
||||
aCount++;
|
||||
i++;
|
||||
while( (c = s[i++]) != 0)
|
||||
{
|
||||
// verbatim if escaped
|
||||
if(c == '\\')
|
||||
string.Cat(s[i++]);
|
||||
else if(c == '>' && !--aCount)
|
||||
break;
|
||||
else
|
||||
string.Cat(c);
|
||||
if(c == '<')
|
||||
aCount++;
|
||||
}
|
||||
if(!expect("ParseAngle", '"', i-1, s))
|
||||
return s.GetCount();
|
||||
string.Cat('>');
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// sigh
|
||||
static bool comma(String const &s, int i)
|
||||
{
|
||||
if(s[i] != ',')
|
||||
return false;
|
||||
if(!i)
|
||||
return true;
|
||||
if(IsDigit(s[i-1]) && IsDigit(s[i+1]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// we can hava a non-quoted string... so we read up
|
||||
// to terminator, which can be '}', ']' or ','
|
||||
int MIValue::ParseUnquotedString(String const &s, int i)
|
||||
{
|
||||
String valStr;
|
||||
int aCount = 0;
|
||||
bool inQuote = false;
|
||||
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && !comma(s, i)) || inQuote || aCount))
|
||||
{
|
||||
valStr.Cat(s[i]);
|
||||
if(s[i] == '\\')
|
||||
{
|
||||
i++;
|
||||
if(s[i])
|
||||
valStr.Cat(s[i++]);
|
||||
continue;
|
||||
}
|
||||
if(s[i] == '<' && !inQuote)
|
||||
aCount++;
|
||||
else if(s[i] == '>' && !inQuote)
|
||||
aCount--;
|
||||
else if(s[i] == '"' && !aCount)
|
||||
inQuote = !inQuote;
|
||||
i++;
|
||||
}
|
||||
type = MIString;
|
||||
string = valStr;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
@ -150,8 +267,12 @@ int MIValue::Parse(String const &s, int i)
|
|||
// otherwise, it can be a sequence of pair name="value" which is stored like a tuple
|
||||
// latter case is an example o bad design of MI interface....
|
||||
Clear();
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
if(s[i] == '"')
|
||||
return ParseString(s, i);
|
||||
else if(s[i] == '<')
|
||||
return ParseAngle(s, i);
|
||||
else if(s[i] == '[')
|
||||
return ParseArray(s, i);
|
||||
else if(s[i] == '{')
|
||||
|
|
@ -165,10 +286,13 @@ int MIValue::Parse(String const &s, int i)
|
|||
{
|
||||
i = ParsePair(name, val, s, i);
|
||||
tuple.Add(name, val);
|
||||
while(s[i] && isspace(s[i]))
|
||||
i++;
|
||||
if(s[i] != ',')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
@ -282,9 +406,22 @@ MIValue &MIValue::Get(int i)
|
|||
{
|
||||
if(IsError())
|
||||
return *this;
|
||||
if(type != MIArray)
|
||||
return ErrorMIValue("Not an Array value type");
|
||||
return array[i];
|
||||
if(type == MIArray)
|
||||
return array[i];
|
||||
if(type == MITuple)
|
||||
return tuple[i];
|
||||
return ErrorMIValue("Not an Array value type");
|
||||
}
|
||||
|
||||
MIValue const &MIValue::Get(int i) const
|
||||
{
|
||||
if(IsError())
|
||||
return *this;
|
||||
if(type == MIArray)
|
||||
return array[i];
|
||||
if(type == MITuple)
|
||||
return tuple[i];
|
||||
return ErrorMIValue("Not an Array value type");
|
||||
}
|
||||
|
||||
MIValue &MIValue::Get(const char *key)
|
||||
|
|
@ -296,6 +433,15 @@ MIValue &MIValue::Get(const char *key)
|
|||
return tuple.Get(key);
|
||||
}
|
||||
|
||||
MIValue const &MIValue::Get(const char *key) const
|
||||
{
|
||||
if(type != MITuple)
|
||||
return ErrorMIValue("Not a Tuple value type");
|
||||
if(tuple.Find(key) < 0)
|
||||
return ErrorMIValue(String("key '") + key + "' not found");
|
||||
return tuple.Get(key);
|
||||
}
|
||||
|
||||
String &MIValue::Get(void)
|
||||
{
|
||||
if(type != MIString)
|
||||
|
|
@ -326,6 +472,21 @@ String MIValue::Get(const char *key, const char *def) const
|
|||
return def;
|
||||
}
|
||||
|
||||
// gets key by index for tuple values
|
||||
String MIValue::GetKey(int idx) const
|
||||
{
|
||||
if(type != MITuple)
|
||||
return ErrorMIValue("Not a Tuple value type");
|
||||
return tuple.GetKey(idx);
|
||||
}
|
||||
|
||||
void MIValue::Set(String const &s)
|
||||
{
|
||||
Clear();
|
||||
type = MIString;
|
||||
string = s;
|
||||
}
|
||||
|
||||
// data dump
|
||||
String MIValue::Dump(int level) const
|
||||
{
|
||||
|
|
@ -333,11 +494,11 @@ String MIValue::Dump(int level) const
|
|||
switch(type)
|
||||
{
|
||||
case MIString:
|
||||
return spacer + string;
|
||||
return spacer + "<STRING>" + string;
|
||||
|
||||
case MITuple:
|
||||
{
|
||||
String s = spacer + "{\n";
|
||||
String s = spacer + "<TUPLE>" + "{\n";
|
||||
level += 4;
|
||||
spacer = String(' ', level);
|
||||
for(int i = 0; i < tuple.GetCount(); i++)
|
||||
|
|
@ -364,7 +525,7 @@ String MIValue::Dump(int level) const
|
|||
|
||||
case MIArray:
|
||||
{
|
||||
String s = spacer + "[ \n";
|
||||
String s = spacer + "<ARRAY>" + "[ \n";
|
||||
level += 4;
|
||||
for(int i = 0; i < array.GetCount(); i++)
|
||||
{
|
||||
|
|
@ -401,3 +562,34 @@ MIValue &MIValue::FindBreakpoint(String const &file, int line)
|
|||
}
|
||||
return NullMIValue();
|
||||
}
|
||||
|
||||
static String PackName(String const &name)
|
||||
{
|
||||
String res;
|
||||
const char *c = ~name;
|
||||
while(*c)
|
||||
{
|
||||
if(!IsSpace(*c))
|
||||
res.Cat(*c);
|
||||
c++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// packs names inside tuples -- to make type recognition easy
|
||||
void MIValue::PackNames(void)
|
||||
{
|
||||
if(type == MITuple)
|
||||
{
|
||||
for(int i = 0; i < tuple.GetCount(); i++)
|
||||
{
|
||||
tuple.SetKey(i, PackName(tuple.GetKey(i)));
|
||||
tuple[i].PackNames();
|
||||
}
|
||||
}
|
||||
else if(type == MIArray)
|
||||
{
|
||||
for(int i = 0; i < array.GetCount(); i++)
|
||||
array[i].PackNames();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,14 @@ typedef enum { MIString, MIArray, MITuple } MIValueType;
|
|||
class MIValue : public Moveable<MIValue>
|
||||
{
|
||||
private:
|
||||
bool expect(String const &where, char exp, int i, String const &s);
|
||||
|
||||
int ParsePair(String &name, MIValue &val, String const &s, int i = 0);
|
||||
int ParseTuple(String const &s, int i = 0);
|
||||
int ParseArray(String const &s, int i = 0);
|
||||
int ParseString(String const &s, int i = 0);
|
||||
int ParseAngle(String const &s, int i = 0);
|
||||
int ParseUnquotedString(String const &s, int i = 0);
|
||||
int ParseValue(String const &s, int i = 0);
|
||||
int Parse(String const &s, int i = 0);
|
||||
|
||||
|
|
@ -45,12 +49,25 @@ class MIValue : public Moveable<MIValue>
|
|||
// simple accessors
|
||||
int GetCount(void) const;
|
||||
int Find(const char *key) const;
|
||||
|
||||
MIValue &Get(int i);
|
||||
MIValue const &Get(int i) const;
|
||||
|
||||
MIValue &operator[](int i) { return Get(i); }
|
||||
MIValue const &operator[](int i) const { return Get(i); }
|
||||
|
||||
MIValue &Get(const char *s);
|
||||
MIValue const &Get(const char *s) const;
|
||||
|
||||
MIValue &operator[](const char *key) { return Get(key); }
|
||||
MIValue const &operator[](const char *key) const { return Get(key); }
|
||||
|
||||
String &Get(void);
|
||||
String const &Get(void) const;
|
||||
|
||||
// gets key by index for tuple values
|
||||
String GetKey(int idx) const;
|
||||
|
||||
operator String&() { return Get(); }
|
||||
operator const String &() const { return Get(); }
|
||||
String &ToString(void) { return Get(); }
|
||||
|
|
@ -60,6 +77,9 @@ class MIValue : public Moveable<MIValue>
|
|||
String Get(const char *key, const char *def) const;
|
||||
String operator()(const char *key, const char *def) const { return Get(key, def); }
|
||||
|
||||
// setter for string (operator= starts parser...)
|
||||
void Set(String const &s);
|
||||
|
||||
// some type checking
|
||||
bool IsArray(void) const { return type == MIArray; }
|
||||
void AssertArray(void) const { ASSERT(type == MIArray); }
|
||||
|
|
@ -73,6 +93,9 @@ class MIValue : public Moveable<MIValue>
|
|||
|
||||
// finds breakpoint data given file and line
|
||||
MIValue &FindBreakpoint(String const &file, int line);
|
||||
|
||||
// packs names inside tuples -- to make type recognition easy
|
||||
void PackNames(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,6 +13,20 @@ def dump(obj):
|
|||
newobj[attr]=dump(newobj[attr])
|
||||
return newobj
|
||||
|
||||
#floating pointer printer
|
||||
class FloatPrinter(object):
|
||||
"Print a floating point number with fixed DOT separator"
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "%.15f" % self.val
|
||||
|
||||
def display_hint(self):
|
||||
return 'String'
|
||||
|
||||
|
||||
# Upp::String printer
|
||||
class UppStringPrinter(object):
|
||||
"Print an Upp::String"
|
||||
|
|
@ -60,24 +74,19 @@ class UppVectorPrinter(object):
|
|||
def __init__ (self, val):
|
||||
self.val = val
|
||||
self.item = val['vector']
|
||||
self.finish = self.item + val['items']
|
||||
self.count = 0
|
||||
self.count = val['items']
|
||||
self.idx = -1
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
count = self.count
|
||||
self.count = self.count + 1
|
||||
if count == 0:
|
||||
return ('.items', self.val['items'])
|
||||
if count == 1:
|
||||
return ('.alloc', self.val['alloc'])
|
||||
if self.item == self.finish:
|
||||
def __next__(self):
|
||||
self.idx += 1
|
||||
if self.idx >= self.count:
|
||||
raise StopIteration
|
||||
elt = self.item.dereference()
|
||||
self.item = self.item + 1
|
||||
return ('[%d]' % (count-2), elt)
|
||||
self.item += 1
|
||||
return ('[%d]' % self.idx, elt)
|
||||
|
||||
def __init__(self, typename, val):
|
||||
self.typename = typename
|
||||
|
|
@ -87,11 +96,10 @@ class UppVectorPrinter(object):
|
|||
return self._iterator(self.val)
|
||||
|
||||
def to_string(self):
|
||||
start = 0
|
||||
finish = self.val['items']
|
||||
end = self.val['alloc']
|
||||
return ('%s count %d alloc %d'
|
||||
% (self.typename, int (finish - start), int (end - start)))
|
||||
count = self.val['items']
|
||||
alloc = self.val['alloc']
|
||||
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
|
||||
% (self.typename, count, alloc))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
|
@ -103,24 +111,19 @@ class UppArrayPrinter(object):
|
|||
def __init__ (self, val):
|
||||
self.val = val['vector']
|
||||
self.item = self.val['vector']
|
||||
self.finish = self.item + self.val['items']
|
||||
self.count = 0
|
||||
self.count = self.val['items']
|
||||
self.idx = -1
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
count = self.count
|
||||
self.count = self.count + 1
|
||||
if count == 0:
|
||||
return ('.items', self.val['items'])
|
||||
if count == 1:
|
||||
return ('.alloc', self.val['alloc'])
|
||||
if self.item == self.finish:
|
||||
def __next__(self):
|
||||
self.idx += 1
|
||||
if self.idx >= self.count:
|
||||
raise StopIteration
|
||||
elt = self.item.dereference().dereference()
|
||||
self.item = self.item + 1
|
||||
return ('[%d]' % (count-2), elt)
|
||||
self.item += + 1
|
||||
return ('[%d]' % self.idx, elt)
|
||||
|
||||
def __init__(self, typename, val):
|
||||
self.typename = typename
|
||||
|
|
@ -130,34 +133,99 @@ class UppArrayPrinter(object):
|
|||
return self._iterator(self.val)
|
||||
|
||||
def to_string(self):
|
||||
start = 0
|
||||
finish = self.val['vector']['items']
|
||||
end = self.val['vector']['alloc']
|
||||
return ('%s count %d alloc %d'
|
||||
% (self.typename, int (finish - start), int (end - start)))
|
||||
count = self.val['vector']['items']
|
||||
alloc = self.val['vector']['alloc']
|
||||
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
|
||||
% (self.typename, count, alloc))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
# Upp::VectorMap and ArrayMap printer
|
||||
class UppMapPrinter(object):
|
||||
"Print an Upp::VectorMap or ArrayMap"
|
||||
# Upp::VectorMap printer
|
||||
class UppVectorMapPrinter(object):
|
||||
"Print an Upp::VectorMap"
|
||||
class _iterator:
|
||||
def __init__ (self, val):
|
||||
self.val = val
|
||||
self.key = val['key']['key']['vector']
|
||||
self.value = val['value']['vector']
|
||||
self.count = val['value']['items']
|
||||
self.idx = -1
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
self.idx += 1
|
||||
if self.idx >= self.count * 2:
|
||||
raise StopIteration
|
||||
if self.idx & 1:
|
||||
aValue = self.value.dereference()
|
||||
self.value += 1
|
||||
return ('value', str(aValue))
|
||||
else:
|
||||
aKey = self.key.dereference()
|
||||
self.key += 1
|
||||
return ('key', str(aKey))
|
||||
|
||||
def __init__(self, typename, val):
|
||||
self.typename = typename
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
return [('.keys', self.val['key']), ('.values', self.val['value'])].__iter__()
|
||||
return self._iterator(self.val)
|
||||
|
||||
def to_string(self):
|
||||
count = self.val['key']['key']['items']
|
||||
alloc = self.val['key']['key']['alloc']
|
||||
return ('%s count %d alloc %d'
|
||||
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
|
||||
% (self.typename, count, alloc))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
return 'map'
|
||||
|
||||
# Upp::ArrayMap printer
|
||||
class UppArrayMapPrinter(object):
|
||||
"Print an Upp::ArrayMap"
|
||||
class _iterator:
|
||||
def __init__ (self, val):
|
||||
self.val = val
|
||||
self.key = val['key']['key']['vector']
|
||||
self.value = val['value']['vector']['vector']
|
||||
self.count = val['value']['vector']['items']
|
||||
self.idx = -1
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
self.idx += 1
|
||||
if self.idx >= self.count * 2:
|
||||
raise StopIteration
|
||||
if self.idx & 1:
|
||||
aValue = self.value.dereference().dereference()
|
||||
self.value += 1
|
||||
return ('value', str(aValue))
|
||||
else:
|
||||
aKey = self.key.dereference()
|
||||
self.key += 1
|
||||
return ('key', str(aKey))
|
||||
|
||||
def __init__(self, typename, val):
|
||||
self.typename = typename
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
return self._iterator(self.val)
|
||||
|
||||
def to_string(self):
|
||||
count = self.val['key']['key']['items']
|
||||
alloc = self.val['key']['key']['alloc']
|
||||
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
|
||||
% (self.typename, count, alloc))
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
# Upp::Value printer
|
||||
class UppValuePrinter(object):
|
||||
|
|
@ -289,6 +357,9 @@ class UppRectPtrPrinter(object):
|
|||
def UppLookupFunction(val):
|
||||
typeStr = str(val.type)
|
||||
|
||||
if typeStr == 'double' or typeStr == 'float' or typeStr == 'long double':
|
||||
return FloatPrinter(val)
|
||||
|
||||
if typeStr == 'Upp::String *':
|
||||
return UppStringPtrPrinter(val)
|
||||
|
||||
|
|
@ -372,11 +443,11 @@ def UppLookupFunction(val):
|
|||
|
||||
regex = re.compile("^Upp::VectorMap<.*>$")
|
||||
if regex.match(lookup_tag):
|
||||
return UppMapPrinter(lookup_tag, val)
|
||||
return UppVectorMapPrinter(lookup_tag, val)
|
||||
|
||||
regex = re.compile("^Upp::ArrayMap<.*>$")
|
||||
if regex.match(lookup_tag):
|
||||
return UppMapPrinter(lookup_tag, val)
|
||||
return UppArrayMapPrinter(lookup_tag, val)
|
||||
|
||||
regex = re.compile("^Upp::Vector<.*>$")
|
||||
if regex.match(lookup_tag):
|
||||
|
|
|
|||
71
uppsrc/ide/Debuggers/TypeSimplify.cpp
Normal file
71
uppsrc/ide/Debuggers/TypeSimplify.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "Debuggers.h"
|
||||
#include <ide/ide.h>
|
||||
|
||||
typedef void (*TYPE_SIMPLIFIER_HANDLER)(MIValue &val);
|
||||
|
||||
static void UppStringSimplify(MIValue &val)
|
||||
{
|
||||
// strings are "easy", in debug mode 's' and 'len' members are provided
|
||||
// so we shall just look at them in tuple
|
||||
try
|
||||
{
|
||||
MIValue &v = val[0][0][0];
|
||||
if(!v.IsTuple())
|
||||
return;
|
||||
if(v.Find("s") < 0)
|
||||
return;
|
||||
String s = v["s"];
|
||||
|
||||
// strip address part
|
||||
int pos = 0;
|
||||
while(s[pos] && s[pos] != '"')
|
||||
pos++;
|
||||
if(!s[pos])
|
||||
return;
|
||||
val.Set(s.Mid(pos));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct TYPE_SIMPLIFIER
|
||||
{
|
||||
const char *type;
|
||||
TYPE_SIMPLIFIER_HANDLER handler;
|
||||
|
||||
};
|
||||
|
||||
TYPE_SIMPLIFIER TYPE_SIMPLIFIERS[] =
|
||||
{
|
||||
{ "<Upp::Moveable<Upp::String,Upp::AString<Upp::String0>>>", UppStringSimplify }
|
||||
};
|
||||
|
||||
#define SIMPLIFIERS_COUNT ((int)(sizeof(TYPE_SIMPLIFIERS) / sizeof(TYPE_SIMPLIFIER)))
|
||||
|
||||
void Gdb_MI2::TypeSimplify(MIValue &val)
|
||||
{
|
||||
static VectorMap<String, TYPE_SIMPLIFIER_HANDLER> handlers;
|
||||
if(!handlers.GetCount())
|
||||
for(int i = 0; i < SIMPLIFIERS_COUNT; i++)
|
||||
handlers.Add(TYPE_SIMPLIFIERS[i].type, TYPE_SIMPLIFIERS[i].handler);
|
||||
|
||||
if(val.IsTuple())
|
||||
{
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
MIValue &v = val[i];
|
||||
if(v.IsTuple() && v.GetCount())
|
||||
{
|
||||
int idx = handlers.Find(v.GetKey(0));
|
||||
if(idx >= 0)
|
||||
handlers[idx](v);
|
||||
else
|
||||
TypeSimplify(v);
|
||||
}
|
||||
else
|
||||
TypeSimplify(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
uppsrc/ide/Debuggers/VarItem.cpp
Normal file
157
uppsrc/ide/Debuggers/VarItem.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#include "Debuggers.h"
|
||||
#include <ide/ide.h>
|
||||
|
||||
// check if value contains an error
|
||||
bool Gdb_MI2::VarItem::IsError(void) const
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// constructor
|
||||
Gdb_MI2::VarItem::VarItem()
|
||||
{
|
||||
error = true;
|
||||
dynamic = false;
|
||||
numChildren = 0;
|
||||
kind = SIMPLE;
|
||||
}
|
||||
|
||||
// copy (pick)
|
||||
Gdb_MI2::VarItem::VarItem(Gdb_MI2::VarItem pick_ &v)
|
||||
{
|
||||
error = v.error;
|
||||
dynamic = v.dynamic;
|
||||
varName = v.varName;
|
||||
shortExpression = v.shortExpression;
|
||||
evaluableExpression = v.evaluableExpression;
|
||||
type = v.type;
|
||||
kind = v.kind;
|
||||
value = v.value;
|
||||
numChildren = v.numChildren;
|
||||
}
|
||||
|
||||
Gdb_MI2::VarItem &Gdb_MI2::VarItem::operator=(pick_ Gdb_MI2::VarItem &v)
|
||||
{
|
||||
error = v.error;
|
||||
dynamic = v.dynamic;
|
||||
varName = v.varName;
|
||||
shortExpression = v.shortExpression;
|
||||
evaluableExpression = v.evaluableExpression;
|
||||
type = v.type;
|
||||
kind = v.kind;
|
||||
value = v.value;
|
||||
numChildren = v.numChildren;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// evaluate an expression usign gdb variables
|
||||
Gdb_MI2::VarItem Gdb_MI2::EvalGdb(String const &expr)
|
||||
{
|
||||
VarItem v;
|
||||
|
||||
MIValue val = MICmd("var-create - @ " + expr);
|
||||
if(val.IsError())
|
||||
return v;
|
||||
v.error = false;
|
||||
v.varName = val["name"];
|
||||
v.evaluableExpression = expr;
|
||||
v.shortExpression = expr;
|
||||
v.value = val["value"];
|
||||
v.numChildren = atoi(val.Get("numchild", "0"));
|
||||
v.type = val["type"];
|
||||
String dispHint = val.Get("displayhint", "");
|
||||
if(ToUpper(dispHint) == "ARRAY")
|
||||
v.kind = VarItem::ARRAY;
|
||||
else if(ToUpper(dispHint) == "MAP")
|
||||
v.kind = VarItem::MAP;
|
||||
else
|
||||
v.kind = VarItem::SIMPLE;
|
||||
v.dynamic = (val.Get("dynamic", "") != "");
|
||||
return v;
|
||||
}
|
||||
|
||||
// remove GDB variable for item
|
||||
void Gdb_MI2::KillVariable(Gdb_MI2::VarItem &v)
|
||||
{
|
||||
MICmd("var-delete " + v.varName);
|
||||
}
|
||||
|
||||
// fetch variable children
|
||||
Vector<Gdb_MI2::VarItem> Gdb_MI2::GetChildren(MIValue const &val, String const &prePath)
|
||||
{
|
||||
Vector<VarItem> res;
|
||||
|
||||
if(val.IsError() || !val.IsTuple())
|
||||
return res;
|
||||
MIValue const &children = val["children"];
|
||||
if(!children.IsArray())
|
||||
return res;
|
||||
|
||||
for(int i = 0; i < children.GetCount(); i++)
|
||||
{
|
||||
MIValue const &child = children[i];
|
||||
|
||||
// for private, protected, public and inherited fake childs, just go deeper
|
||||
String exp = child["exp"];
|
||||
String typ = child.Get("type", "");
|
||||
String nam = child.Get("name");
|
||||
if(exp == "private" || exp == "protected" || exp == "public" || exp == typ)
|
||||
{
|
||||
MIValue val2 = MICmd("var-list-children 1 " + nam);
|
||||
res.Append(GetChildren(val2, prePath));
|
||||
}
|
||||
else
|
||||
{
|
||||
VarItem &v = res.Add();
|
||||
v.error = false;
|
||||
v.varName = nam;
|
||||
v.shortExpression = prePath + "." + exp;
|
||||
v.type = typ;
|
||||
v.value = child["value"];
|
||||
v.dynamic = (child.Get("dynamic", "") != "");
|
||||
v.numChildren = atoi(child.Get("numchild", "0"));
|
||||
|
||||
String dispHint = child.Get("displayhint", "");
|
||||
if(ToUpper(dispHint) == "ARRAY")
|
||||
v.kind = VarItem::ARRAY;
|
||||
else if(ToUpper(dispHint) == "MAP")
|
||||
v.kind = VarItem::MAP;
|
||||
else
|
||||
v.kind = VarItem::SIMPLE;
|
||||
/*
|
||||
MIValue vExp = MICmd("var-info-path-expression " + nam);
|
||||
v.evaluableExpression = vExp.Get("path_expr", "");
|
||||
*/
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// fetch variable children
|
||||
Vector<Gdb_MI2::VarItem> Gdb_MI2::GetChildren(Gdb_MI2::VarItem &v, int minChild, int maxChild)
|
||||
{
|
||||
// for 'simple' (i.e. not maps or arrays..) get all children
|
||||
// otherwise get the given range
|
||||
int first, last;
|
||||
if(v.kind == VarItem::SIMPLE)
|
||||
{
|
||||
first = 0;
|
||||
last = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(minChild < 0)
|
||||
first = 0;
|
||||
else
|
||||
first = minChild;
|
||||
if(maxChild < 0)
|
||||
last = v.numChildren;
|
||||
else
|
||||
last = maxChild;
|
||||
}
|
||||
MIValue val = MICmd(Format("var-list-children 1 %s %d %d", v.varName, first, last));
|
||||
return GetChildren(val, v.shortExpression);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue