From 5034466cdac337da4bab33a610c7696035abdc90 Mon Sep 17 00:00:00 2001 From: micio Date: Wed, 19 Feb 2014 10:33:22 +0000 Subject: [PATCH] Ide/Debuggers/Gdb_MI2 : refactoring git-svn-id: svn://ultimatepp.org/upp/trunk@6928 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/ide/Debuggers/Gdb_MI2.cpp | 341 +++++++++++++++------ uppsrc/ide/Debuggers/Gdb_MI2.h | 64 ++-- uppsrc/ide/Debuggers/Gdb_MI2Eval.cpp | 217 ++++++++++--- uppsrc/ide/Debuggers/MIValue.cpp | 105 ++++++- uppsrc/ide/Debuggers/MIValue.h | 10 + uppsrc/ide/Debuggers/TypeSimplify.cpp | 22 ++ uppsrc/ide/Debuggers/TypeSimplify.h | 10 +- uppsrc/ide/Debuggers/UppSimplifiers.icpp | 373 +++++++++++++++-------- 8 files changed, 840 insertions(+), 302 deletions(-) diff --git a/uppsrc/ide/Debuggers/Gdb_MI2.cpp b/uppsrc/ide/Debuggers/Gdb_MI2.cpp index ccbb92cd1..2adb42266 100644 --- a/uppsrc/ide/Debuggers/Gdb_MI2.cpp +++ b/uppsrc/ide/Debuggers/Gdb_MI2.cpp @@ -1,6 +1,7 @@ #include "Debuggers.h" #include +#include "TypeSimplify.h" #include "PrettyPrinters.brc" #define LDUMP(x) // DDUMP(x) @@ -35,6 +36,24 @@ void WatchEdit::HighlightLine(int line, Vector& h, int pos) h[i].paper = (line % 2 ? cOdd : cEven); } +// fill a pane with data from a couple of arrays without erasing it first +// (avoid re-painting and resetting scroll if not needed) +static void FillPane(ArrayCtrl &pane, Index const &nam, Vector const &val) +{ + int oldCount = pane.GetCount(); + int newCount = nam.GetCount(); + if(newCount < oldCount) + for(int i = oldCount - 1; i >= newCount; i--) + pane.Remove(i); + for(int i = 0; i < min(oldCount, newCount); i++) + { + pane.Set(i, 0, nam[i]); + pane.Set(i, 1, val[i]); + } + for(int i = oldCount; i < newCount; i++) + pane.Add(nam[i], val[i]); +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC IDE INTERFACE ///////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -130,11 +149,10 @@ void Gdb_MI2::Run() // non-stop mode enabled crashes X...don't know if it's a GDB bug or Theide one. // anyways, by now we give up with async mode and remove 'Asynchronous break' function val = MICmd("exec-run"); - // val = MICmd("interpreter-exec console run&"); - else val = MICmd("exec-continue --all"); + int i = 50; while(!started && --i) { @@ -200,33 +218,31 @@ 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) - { - idx = thisNames.Find(exp); - if(idx < 0) - return false; - nam = thisFullNames[idx]; - val = thisValues[idx]; - } + int iVal; + String val; + + // first look into locals.... + iVal = localExpressions.Find(exp); + if(iVal >= 0) + val = localValues[iVal]; + // if not fount, look into 'this' else { - nam = exp; - val = localVarValues[idx]; + iVal = thisShortExpressions.Find(exp); + if(iVal >= 0) + val = thisValues[iVal]; + else + return false; } mt.display = &StdDisplay(); - mt.value = nam + "=" + val; + mt.value = exp + "=" + val; mt.sz = mt.display->GetStdSize(String(mt.value) + "X"); return true; -*/ -return false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -293,6 +309,7 @@ Gdb_MI2::Gdb_MI2() watches.WhenLeftDouble = THISBACK1(onExploreExpr, &watches); watches.EvenRowColor(); watches.OddRowColor(); + watches.WhenUpdateRow = THISBACK1(SyncWatches, MIValue()); int c = EditField::GetStdHeight(); explorer.AddColumn("", 1); @@ -530,7 +547,7 @@ MIValue Gdb_MI2::ReadGdb(bool wait) // some commands (in particular if they return python exceptions) // have a delay between returned exception text and command result // so we shall wait up to the final (gdb) - int retries = 3 * 50; + int retries = 13 * 50; while(dbg && retries--) { dbg->Read(s); @@ -543,7 +560,6 @@ MIValue Gdb_MI2::ReadGdb(bool wait) } else if(dbg) dbg->Read(output); - if(output.IsEmpty()) return res; return ParseGdb(output); @@ -610,8 +626,8 @@ MIValue Gdb_MI2::MICmd(const char *cmdLine) // is there and gives problems to MI interface. We shall maybe // parse and store it somewhere ReadGdb(false); - dbg->Write(String("-") + cmdLine + "\n"); + dbg->Write(String("-") + cmdLine + "\n"); return ReadGdb(); } @@ -743,7 +759,7 @@ void Gdb_MI2::SyncIde(bool fr) { // kill pending update callbacks timeCallback.Kill(); - + // get current frame info and level MIValue fInfo = MICmd("stack-info-frame")["frame"]; int level = atoi(fInfo["level"].Get()); @@ -763,22 +779,24 @@ void Gdb_MI2::SyncIde(bool fr) } // setup threads droplist - threadSelector.Clear(); - MIValue tInfo = MICmd("thread-info"); - if(!tInfo.IsError() && !tInfo.IsEmpty()) { - int curThread = atoi(tInfo["current-thread-id"].Get()); - MIValue &threads = tInfo["threads"]; - for(int iThread = 0; iThread < threads.GetCount(); iThread++) + threadSelector.Clear(); + MIValue tInfo = MICmd("thread-info"); + if(!tInfo.IsError() && !tInfo.IsEmpty()) { - int id = atoi(threads[iThread]["id"].Get()); - if(id == curThread) + int curThread = atoi(tInfo["current-thread-id"].Get()); + MIValue &threads = tInfo["threads"]; + for(int iThread = 0; iThread < threads.GetCount(); iThread++) { - threadSelector.Add(id, Format("#%03x(*)", id)); - break; + int id = atoi(threads[iThread]["id"].Get()); + if(id == curThread) + { + threadSelector.Add(id, Format("#%03x(*)", id)); + break; + } } + threadSelector <<= curThread; } - threadSelector <<= curThread; } // get the arguments for current frame @@ -791,11 +809,11 @@ void Gdb_MI2::SyncIde(bool fr) frame <<= 0; } - // update vars and disasm only on idle times - timeCallback.Set(500, THISBACK(SyncData)); -//SyncData(); - + // sync disassembly SyncDisas(fInfo, fr); + + // update vars only on idle times + timeCallback.Set(500, THISBACK(SyncData)); } // logs frame data on console @@ -841,9 +859,8 @@ 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) { @@ -1085,34 +1102,111 @@ struct CapitalLess }; // update local variables on demand -void Gdb_MI2::UpdateLocalVars(void) +void Gdb_MI2::SyncLocals(MIValue val) { - localVarNames.Clear(); - localVarValues.Clear(); - - MIValue iLoc = MICmd("stack-list-variables 1"); - if(!iLoc || iLoc.IsEmpty()) - return; - MIValue &loc = iLoc["variables"]; - if(!loc.IsArray()) - return; - - for(int iLoc = 0; iLoc < loc.GetCount(); iLoc++) + bool firstCall = false; + static VectorMap prev; + + // if not done, start first evaluation step + if(val.IsEmpty()) { - MIValue &curLoc = loc[iLoc]; - if(curLoc["value"].IsString()) + prev = DataMap2(locals); + + // get local vars names + MIValue locs = MICmd("stack-list-variables 1"); + if(!locs.IsTuple() || locs.Find("variables") < 0) + return; + + // variables are returned as a tuple, named "variables" + // containing a array of variables + MIValue lLocs = locs["variables"]; + if(!lLocs.IsArray()) + return; + + // as values are in string format we must parse them + // so we simply build a big string with all variables + // and feed into MIValue parser + String s = "{"; + for(int iLoc = 0; iLoc < lLocs.GetCount(); iLoc++) { - localVarNames.Add(curLoc["name"].ToString()); - localVarValues.Add(curLoc["value"].ToString()); + MIValue &lLoc = lLocs[iLoc]; + String name = lLoc["name"]; + + // skip 'this', we've a page for it + if(name == "this") + continue; + s << name << "=" << lLoc["value"] << ","; } - } + if(s.EndsWith(",")) + s = s.Left(s.GetCount()-1); + s << "}"; + val = s; + + val.PackNames(); + AddAttribs("", val); + val.FixArrays(); + + TypeSimplify(val, false); + + firstCall = true; + } + + bool more = TypeSimplify(val, true); + + // collect variables names and values + CollectVariables(val, localExpressions, localValues, localHints); + + // update locals pane + FillPane(locals, localExpressions, localValues); + + if(more) + // more evaluations are needed, respawn the function with a delay + // big delay on first respawn to allow quick stepping without lag + timeCallback.Set(firstCall ? 2000 : 200, THISBACK1(SyncLocals, val)); + else + // when finished, mark changed values + MarkChanged2(prev, locals); } // update 'this' inspector data -void Gdb_MI2::UpdateThis(void) +void Gdb_MI2::SyncThis(MIValue val) { - MIValue val = Evaluate("*this"); + bool firstCall = false; + static VectorMap prev; + + // if not done, start first evaluation step + if(val.IsEmpty()) + { + val = Evaluate("*this", false); + prev = DataMap2(members); + + // this is the first evaluation step + // just simple types -- lag some seconds to full evaluation + // which is quite slow + firstCall = true; + } + + // deep evaluation step + bool more = TypeSimplify(val, true); + + // collect variables names and values CollectVariables(val, thisExpressions, thisValues, thisHints); + + // strip the (*this) from variables names + thisShortExpressions.Clear(); + for(int iExpr = 0; iExpr < thisExpressions.GetCount(); iExpr++) + thisShortExpressions.Add(thisExpressions[iExpr].Mid(8)); + + // update 'this' pane + FillPane(members, thisShortExpressions, thisValues); + + if(more) + // more evaluations are needed, respawn the function with a delay + // big delay on first respawn to allow quick stepping without lag + timeCallback.Set(firstCall ? 2000 : 200, THISBACK1(SyncThis, val)); + else + // when finished, mark changed values + MarkChanged2(prev, members); } // sync auto vars treectrl @@ -1139,9 +1233,9 @@ void Gdb_MI2::SyncAutos() break; exp << p.ReadId(); } - int idx = localVarNames.Find(exp); + int idx = localExpressions.Find(exp); if(idx >= 0) - autos.Add(exp, localVarValues[idx]); + autos.Add(exp, localValues[idx]); } p.SkipTerm(); } @@ -1150,40 +1244,72 @@ void Gdb_MI2::SyncAutos() } // sync watches treectrl -void Gdb_MI2::SyncLocals() +void Gdb_MI2::SyncWatches(MIValue val) { - VectorMap 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 prev = DataMap2(members); - members.Clear(); - - for(int iMem = 0; iMem < thisExpressions.GetCount(); iMem++) - members.Add(thisExpressions[iMem].Mid(7), thisValues[iMem]); - MarkChanged2(prev, members); -} - -// sync watches treectrl -void Gdb_MI2::SyncWatches() -{ - // re-fill the control - VectorMap prev = DataMap2(watches); - for(int i = 0; i < watches.GetCount(); i++) + static VectorMap prev; + bool firstCall = false; + + // on first cycle, we fast evaluate all expressions + // without going deep, and put all results into an array + if(val.IsEmpty()) { - MIValue res = MICmd("data-evaluate-expression \"" + watches.Get(i, 0).ToString() + "\""); - if(res.IsError() || res.IsEmpty()) - watches.Set(i, 2, t_("")); - else - watches.Set(i, 2, res["value"].ToString()); + prev = DataMap2(watches); + + for(int i = 0; i < watches.GetCount(); i++) + { + String expr = watches.Get(i,0); + MIValue res; + res.Set(""); + MIValue valExpr = MICmd("data-evaluate-expression " + expr); + + if(valExpr.IsTuple() && valExpr.Find("value") >= 0) + { + MIValue const &tup = valExpr.Get("value"); + if(tup.IsString()) + { + String s = tup.ToString(); + res = s; + } + } + res.PackNames(); + AddAttribs("", res); + res.FixArrays(); + + val.Add(res); + } + firstCall = true; } - MarkChanged2(prev, watches); +RLOG(val.Dump()); + bool more = false; + + // simplify loop, returns when at least one simplify step happens + for(int iVal = 0; iVal < val.GetCount(); iVal++) + { + MIValue &v = val[iVal]; + more |= TypeSimplify(v, true); + if(more) + break; + } + // collects all results + watchesValues.Clear(); + for(int iVal = 0; iVal < val.GetCount(); iVal++) + watchesValues << CollectVariablesShort(val[iVal]); + + // fill expressions with ctrl expr contents + watchesExpressions.Clear(); + for(int iVal = 0; iVal < watchesValues.GetCount(); iVal++) + watchesExpressions << watches.Get(iVal, 0); + + // update locals pane + FillPane(watches, watchesExpressions, watchesValues); + + if(more) + // more evaluations are needed, respawn the function with a delay + // big delay on first respawn to allow quick stepping without lag + timeCallback.Set(firstCall ? 2000 : 200, THISBACK1(SyncWatches, val)); + else + // when finished, mark changed values + MarkChanged2(prev, watches); } // sync data tabs, depending on which tab is shown @@ -1192,17 +1318,15 @@ void Gdb_MI2::SyncData() if(dataSynced) return; - // updates stored local variables - // those are needed for Autos, Locals and Tooltips - UpdateLocalVars(); - // update stored 'this' member data // also used for tooltips and 'this' pane page - UpdateThis(); + SyncThis(); - SyncAutos(); + // updated locals variables SyncLocals(); - SyncMembers(); + + SyncAutos(); + SyncWatches(); dataSynced = true; @@ -1515,7 +1639,8 @@ void Gdb_MI2::onExploreExpr(ArrayCtrl *what) { // otherwise, we use the expression from sending ArrayCtrl int line = what->GetCursor(); - if(line >= 0) + int col = what->GetClickColumn(); + if(line >= 0 && (what != &watches || col != 0)) expr = what->Get(line, 0); } // nothing to do on empty expression @@ -1620,6 +1745,24 @@ bool Gdb_MI2::Create(One _host, const String& exefile, const String& cmdli MICmd("gdb-set confirm off"); MICmd("gdb-set print asm-demangle"); + // limit array printing to 200 elements + MICmd("gdb-set print elements 200"); + + // do NOT print repeated counts -- they confuse the parser + MICmd("gdb-set print repeat 0"); + + // we wanna addresses, we skip them in parser if useless +// MICmd("gdb-set print address off"); + + // print true objects having their pointers (if have vtbls) + MICmd("gdb-set print object on"); + + // print symbolic addresses, if found + MICmd("gdb-set print symbol on"); + + // do not search of symbolic address near current one + MICmd("gdb-set print max-symbolic-offset 1"); + // avoids debugger crash if caught inside ungrabbing function // (don't solves all cases, but helps...) MICmd("gdb-set unwindonsignal on"); diff --git a/uppsrc/ide/Debuggers/Gdb_MI2.h b/uppsrc/ide/Debuggers/Gdb_MI2.h index a4bd26e9a..17de7c8f9 100644 --- a/uppsrc/ide/Debuggers/Gdb_MI2.h +++ b/uppsrc/ide/Debuggers/Gdb_MI2.h @@ -14,7 +14,7 @@ class WatchEdit : public LineEdit class Gdb_MI2 : public Debugger, public ParentCtrl { private: - + // item for a GDB variable struct VarItem : Moveable { @@ -139,9 +139,6 @@ class Gdb_MI2 : public Debugger, public ParentCtrl MIValue ParseGdb(String const &s, bool wait = true); MIValue ReadGdb(bool wait = true); - // sends an MI command and get answer back - MIValue MICmd(const char *cmdLine); - // format breakpoint line from ide file and line String BreakPos(String const &file, int line); @@ -158,27 +155,24 @@ class Gdb_MI2 : public Debugger, public ParentCtrl MIValue InsertBreakpoint(const char *file, int line); // stored local variable expressions, and values - IndexlocalVarNames; - VectorlocalVarValues; + IndexlocalExpressions; + VectorlocalValues; + VectorlocalHints; // stored watches expressions and values IndexwatchesExpressions; VectorwatchesValues; + VectorwatchesHints; // 'this' variable inspection data - VectorthisExpressions; + IndexthisExpressions; VectorthisValues; VectorthisHints; + IndexthisShortExpressions; // stored autos expressions, values and types String autoLine; - // update local variables on demand - void UpdateLocalVars(void); - - // update 'this' inspector data - void UpdateThis(void); - // logs frame data on console void LogFrame(String const &msg, MIValue &frame); @@ -207,13 +201,13 @@ class Gdb_MI2 : public Debugger, public ParentCtrl void SyncAutos(); // sync local variables pane - void SyncLocals(void); + void SyncLocals(MIValue val = MIValue()); - // sync 'this' members pane - void SyncMembers(void); + // Sync 'this' inspector data + void SyncThis(MIValue val = MIValue()); // sync watches treectrl - void SyncWatches(); + void SyncWatches(MIValue val = MIValue()); // sync data tabs, depending on which tab is shown bool dataSynced; @@ -270,15 +264,26 @@ class Gdb_MI2 : public Debugger, public ParentCtrl String GetHostPath(const String& path) { return host->GetHostPath(path); } String GetLocalPath(const String& path) { return host->GetLocalPath(path); } - // known types simplifier - // takes a MIValue from '-data-evaluate-expression' command and try - // do simplify diplay of known types - void TypeSimplify(MIValue &val); + // now we scan the result and add some info + // so, for example, if we find tuple like this one: + // data = simplevalue + // this will be modified as + // data = { value = simplevalue, expr = evaluable_expression } + // and for a complex value + // data = { some=complex, not_simple=val } + // woll be modified as + // data = { = { some=complex, not_simple=val }, = evaluable_expression } + // More attributes will be added by type simplifier phase + void AddAttribs(String const &expr, MIValue &valExpr); // collects evaluated variables got with Evaluate // hints are used to choose the visualizer when deep-inspecting members // 0 for simple values, 1 for arrays, 2 for map - void CollectVariables(MIValue &val, Vector &exprs, Vector &vals, Vector &hints); + void CollectVariables(MIValue &val, Index &exprs, Vector &vals, Vector &hints); + + // collect evaluated variables got with Evaluate + // into a single-line string for short display + String CollectVariablesShort(MIValue &val); protected: @@ -301,13 +306,24 @@ class Gdb_MI2 : public Debugger, public ParentCtrl Gdb_MI2(); virtual ~Gdb_MI2(); - + + // sends an MI command and get answer back + MIValue MICmd(const char *cmdLine); + + // known types simplifier + // takes a MIValue from '-data-evaluate-expression' command and try + // do simplify diplay of known types + // with deep = false it does just type simplification, no deep evaluation of containers + // with deep = true it does ONE deep evaluation step + // returns true if more deep evaluation steps are needed, false otherwise + bool TypeSimplify(MIValue &val, bool deep); // variable inspection support // returns a MIValue with inspected data and some info fields added // and known types simplified and cathegorized // unknown and simple types are left as they are - MIValue Evaluate(String expr); + // deep is true if we shall have a complete sub-elements evaluation + MIValue Evaluate(String expr, bool deep = false); }; #endif diff --git a/uppsrc/ide/Debuggers/Gdb_MI2Eval.cpp b/uppsrc/ide/Debuggers/Gdb_MI2Eval.cpp index d83281447..acc6ff8dc 100644 --- a/uppsrc/ide/Debuggers/Gdb_MI2Eval.cpp +++ b/uppsrc/ide/Debuggers/Gdb_MI2Eval.cpp @@ -13,7 +13,7 @@ // woll be modified as // data = { = { some=complex, not_simple=val }, = evaluable_expression } // More attributes will be added by type simplifier phase -static void AddAttribs(String const &expr, MIValue &valExpr) +void Gdb_MI2::AddAttribs(String const &expr, MIValue &valExpr) { if(valExpr.IsTuple()) { @@ -22,7 +22,12 @@ static void AddAttribs(String const &expr, MIValue &valExpr) String nam = valExpr.GetKey(i); String nExpr; if(!nam.StartsWith("<")) - nExpr = expr + "." + nam; + { + if(expr != "") + nExpr = expr + "." + nam; + else + nExpr = nam; + } else nExpr = expr; MIValue v = valExpr[i]; @@ -34,11 +39,14 @@ static void AddAttribs(String const &expr, MIValue &valExpr) } else if(valExpr.IsArray()) { + for(int i = 0; i < valExpr.GetCount(); i++) + AddAttribs(expr, valExpr[i]); } } -void Gdb_MI2::TypeSimplify(MIValue &val) +bool Gdb_MI2::TypeSimplify(MIValue &val, bool deep) { + bool needMore = false; if(val.IsTuple()) { for(int i = 0; i < val.GetCount(); i++) @@ -58,24 +66,56 @@ void Gdb_MI2::TypeSimplify(MIValue &val) { TYPE_SIMPLIFIER_HANDLER handler = GetSimplifier(v.GetKey(0)); if(handler) - handler(*this, vRoot); + { + needMore |= handler(*this, vRoot, deep); + if(deep) + { + if(needMore) + { + // we shall remove the temporary value now... + vRoot.Remove(SIMPLIFY_TEMPVAL); + return needMore; + } + } + else if(needMore) + vRoot.FindAdd(SIMPLIFY_TEMPVAL, ""); + } else - TypeSimplify(v); + needMore |= TypeSimplify(v, deep); } else - TypeSimplify(v); + needMore |= TypeSimplify(v, deep); } else - TypeSimplify(v); + needMore |= TypeSimplify(v, deep); } } + else if(val.IsArray()) + { + for(int i = 0; i < val.GetCount(); i++) + { + // encapsulate each element inside a tuple + MIValue tuple; + tuple.Add("", val[i]); + + // simplify + needMore |= TypeSimplify(tuple, deep); + + // de-encapsulate the element + val[i] = tuple[0]; + if(deep && needMore) + return needMore; + } + } + return needMore; } // variable inspection support // returns a MIValue with inspected data and some info fields added // and known types simplified and cathegorized // unknown and simple types are left as they are -MIValue Gdb_MI2::Evaluate(String expr) +// if deep is true, partial evaluation of containers is done +MIValue Gdb_MI2::Evaluate(String expr, bool deep) { // add parhentesis around expression... gdb is dumb expr = "(" + expr + ")"; @@ -83,7 +123,7 @@ MIValue Gdb_MI2::Evaluate(String expr) // ask gdb to evaluate expression // and gather result in a tuple MIValue valExpr = MICmd("data-evaluate-expression " + expr); - + // return empty value on error if(!valExpr.IsTuple()) return MIValue(); @@ -97,25 +137,24 @@ MIValue Gdb_MI2::Evaluate(String expr) String s = tup.ToString(); MIValue parsed(s); - // enclose it in a dummy container - MIValue enclosed; - enclosed.Add("", parsed); + // pack tuple names--remove spaces + parsed.PackNames(); - // remove spaces from names - enclosed.PackNames(); - - // Add attributes to expression data - AddAttribs(expr, enclosed); - +//RLOG(parsed.Dump()); + AddAttribs(expr, parsed); + + // fix arrays + parsed.FixArrays(); + +//RLOG(parsed.Dump()); // now we go through the type simplifier, which try to give simple representation - // for known types - TypeSimplify(enclosed); - -//RLOG(enclosed.Dump()); - - // now we can de-enclose it... - parsed = enclosed[""][SIMPLIFY_VALUE]; + // for known types -- first step, just type simplification and NOT deep evaluation + bool needMore = TypeSimplify(parsed, false); + // deep container evaluation on request + if(deep) + while(needMore) + needMore = TypeSimplify(parsed, true); //RLOG(parsed.Dump()); // and finally return the cleaned evaluated data @@ -137,7 +176,7 @@ RLOG("WEIRD STUFF HERE...."); // collects evaluated variables got with Evaluate // hints are used to choose the visualizer when deep-inspecting members // 0 for simple values, 1 for arrays, 2 for map -static void Collect0(MIValue &val, Vector &exprs, Vector &vals, Vector &hints) +static void Collect0(MIValue &val, Index &exprs, Vector &vals, Vector &hints) { if(!val.IsTuple()) return; @@ -145,33 +184,44 @@ static void Collect0(MIValue &val, Vector &exprs, Vector &vals, for(int i = 0; i < val.GetCount(); i++) { MIValue &v = val[i]; - MIValue &data = v[SIMPLIFY_VALUE]; - if(data.IsError()) - continue; - if(data.IsString()) + + // variables still to be evaluated... + if(v.Find(SIMPLIFY_TEMPVAL) >= 0) { - String d = data.ToString(); - if(!d.StartsWith("<")) - { - vals.Add(data.ToString()), - exprs.Add(v[SIMPLIFY_EXPR]); - String hint = v.Get(SIMPLIFY_HINT, SIMPLIFY_SIMPLE); - if(hint == SIMPLIFY_SIMPLE) - hints << 0; - else if(hint == SIMPLIFY_ARRAY) - hints << 1; - else if(hint == SIMPLIFY_MAP) - hints << 2; - else - hints << 0; - } + vals.Add(v[SIMPLIFY_TEMPVAL].ToString()); + exprs.Add(v[SIMPLIFY_EXPR].ToString()); + hints << 0; + } + else + { + MIValue &data = v[SIMPLIFY_VALUE]; + if(data.IsError()) + continue; + if(data.IsString()) + { + String d = data.ToString(); + if(!d.StartsWith("<")) + { + vals.Add(data.ToString()), + exprs.Add(v[SIMPLIFY_EXPR].ToString()); + String hint = v.Get(SIMPLIFY_HINT, SIMPLIFY_SIMPLE); + if(hint == SIMPLIFY_SIMPLE) + hints << 0; + else if(hint == SIMPLIFY_ARRAY) + hints << 1; + else if(hint == SIMPLIFY_MAP) + hints << 2; + else + hints << 0; + } + } + else if(data.IsTuple()) + Collect0(data, exprs, vals, hints); } - else if(data.IsTuple()) - Collect0(data, exprs, vals, hints); } } -void Gdb_MI2::CollectVariables(MIValue &val, Vector &exprs, Vector &vals, Vector &hints) +void Gdb_MI2::CollectVariables(MIValue &val, Index &exprs, Vector &vals, Vector &hints) { exprs.Clear(); vals.Clear(); @@ -180,3 +230,74 @@ void Gdb_MI2::CollectVariables(MIValue &val, Vector &exprs, Vector= 0) + s << expr << "=" << v[SIMPLIFY_TEMPVAL].ToString() << " , "; + else + { + MIValue &data = v[SIMPLIFY_VALUE]; + if(data.IsError()) + return; + if(data.IsString()) + { + // skip vtbls... quite useless + if(expr.StartsWith("_vptr.")) + return; + + String d = data.ToString(); + if(!d.StartsWith("<")) + s << v[SIMPLIFY_EXPR].ToString() << "=" << d << " , "; + } + else if(data.IsTuple()) + CollectShort0(data, s); + } + } + } + else if(val.IsArray()) + { + for(int i = 0; i < val.GetCount(); i++) + { + MIValue &v = val[i]; + + if(v.Find(SIMPLIFY_TEMPVAL) >= 0) + s << v[SIMPLIFY_TEMPVAL].ToString() << " , "; + else + { + MIValue &data = v[SIMPLIFY_VALUE]; + if(data.IsError()) + return; + if(data.IsString()) + { + String d = data.ToString(); + if(!d.StartsWith("<")) + s << d << " , "; + } + else if(data.IsTuple()) + CollectShort0(data, s); + } + } + } +} + +// collect evaluated variables got with Evaluate +// into a vector of single-line string for short display +String Gdb_MI2::CollectVariablesShort(MIValue &val) +{ + String s; + + CollectShort0(val, s); + if(s == "") + s = ""; + else if(s.EndsWith(" , ")) + s = s.Left(s.GetCount()-3); + return s; +} diff --git a/uppsrc/ide/Debuggers/MIValue.cpp b/uppsrc/ide/Debuggers/MIValue.cpp index 57763ac6e..1beff6ab9 100644 --- a/uppsrc/ide/Debuggers/MIValue.cpp +++ b/uppsrc/ide/Debuggers/MIValue.cpp @@ -32,15 +32,33 @@ bool MIValue::expect(String const &where, char exp, int i, String const &s) static char backslash(String const &s, int &i) { i++; - char c; if(IsDigit(s[i])) { - c = (s[i]-'0')*64 + (s[i+1]-'0')*8 + s[i+2]-'0'; + char c = (s[i]-'0')*64 + (s[i+1]-'0')*8 + s[i+2]-'0'; i += 2; + return c; + } + + // control chars and octals + switch(s[i]) + { + case 'a' : + return '\a'; + case 'b' : + return '\b'; + case 'f' : + return '\f'; + case 'n' : + return '\n'; + case 'r' : + return '\r'; + case 't' : + return '\t'; + case 'v' : + return '\v'; + default: + return s[i]; } - else - c = s[i]; - return c; } @@ -58,11 +76,18 @@ int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i) // is starting wirh '[' or '{' take it as a value with empty name if(s[i] == '{' || s[i] == '[') + { +/* +RLOG("STICAZZI...."); +RLOG("i = " << i << " s = " << s); +*/ name = ""; + return val.ParseTuple(s, i); + } else { int aCount = 0; - while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']') || aCount)) + while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && s[i] != ',') || aCount)) { if(s[i] == '<') aCount++; @@ -97,6 +122,18 @@ int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i) while(s[i] && isspace(s[i])) i++; } + + // skip address part, if any... it's useless and confuses the parser + if(s[i] == '@') + { + while(s[i] && s[i] != ':') + i++; + if(s[i] == ':') + i++; + while(s[i] && IsSpace(s[i])) + i++; + } + switch(s[i]) { case '"': @@ -271,6 +308,7 @@ 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]); @@ -533,13 +571,28 @@ void MIValue::Set(String const &s) #define MARK_ARRAY "" #define MARK_TUPLE "" #endif + +// dumps a string with special chars inside +String MIValue::Dump(String const &s) +{ + String res; + for(int i = 0; i < s.GetCharCount(); i++) + { + byte c = s[i]; + if(isprint(c)) + res << c; + else + res += Format("\\%03o", c); + } + return res; +} String MIValue::Dump(int level) const { String spacer(' ', level); switch(type) { case MIString: - return spacer + MARK_STRING + string; + return spacer + MARK_STRING + Dump(string); case MITuple: { @@ -639,6 +692,35 @@ void MIValue::PackNames(void) } } +// fix arrays -- i.e. replace a tuple containing ALL unnamed elements +// with the corresponding array +// ( gdb evaluator array data is returned as tuple with {} instead []= +void MIValue::FixArrays(void) +{ + + bool named = false; + if(IsTuple()) + { + for(int iVal = 0; iVal < tuple.GetCount(); iVal++) + if(tuple.GetKey(iVal) != "") + { + named = true; + break; + } + if(!named) + { + array.Clear(); + for(int iVal = 0; iVal < tuple.GetCount(); iVal++) + array.Add(tuple[iVal]); + tuple.Clear(); + type = MIArray; + } + } + if(IsTuple() || IsArray()) + for(int i = 0; i < GetCount(); i++) + Get(i).FixArrays(); +} + // add an item to a tuple MIValue &MIValue::Add(String const &key, MIValue pick_ &v) { @@ -699,3 +781,12 @@ MIValue &MIValue::Add(String const &data) v.Set(data); return Add(v); } + +// remove a tuple key +MIValue &MIValue::Remove(String const &key) +{ + if(type != MITuple) + return *this; + tuple.RemoveKey(key); + return *this; +} diff --git a/uppsrc/ide/Debuggers/MIValue.h b/uppsrc/ide/Debuggers/MIValue.h index e84f39ae0..5f4511c09 100644 --- a/uppsrc/ide/Debuggers/MIValue.h +++ b/uppsrc/ide/Debuggers/MIValue.h @@ -88,6 +88,9 @@ class MIValue : public Moveable bool IsString(void) const { return type == MIString; } void AssertString(void) const { ASSERT(type == MIString); } + // dumps a string with special chars inside + static String Dump(String const &s); + // data dump String Dump(int level = 0) const; @@ -97,6 +100,10 @@ class MIValue : public Moveable // packs names inside tuples -- to make type recognition easy void PackNames(void); + // fix arrays -- i.e. replace a tuple containing ALL unnamed elements + // with the corresponding array + void FixArrays(void); + // add some data to a value // add an item to a tuple @@ -107,6 +114,9 @@ class MIValue : public Moveable // add an item to an array MIValue &Add(MIValue pick_ &v); MIValue &Add(String const &data); + + // remove a tuple key + MIValue &Remove(String const &key); }; #endif diff --git a/uppsrc/ide/Debuggers/TypeSimplify.cpp b/uppsrc/ide/Debuggers/TypeSimplify.cpp index 6f45214fe..9c7eff8e1 100644 --- a/uppsrc/ide/Debuggers/TypeSimplify.cpp +++ b/uppsrc/ide/Debuggers/TypeSimplify.cpp @@ -21,3 +21,25 @@ TYPE_SIMPLIFIER_HANDLER GetSimplifier(String const &pattern) } return NULL; } + +// next index when stepping through containers values +int SIMPLIFIER_NEXT_INDEX(MIValue &val, int total) +{ + int step; + + int idx = val.Find(SIMPLIFY_STEP); + if(idx < 0) + { + step = 0; + val.Add(SIMPLIFY_STEP, "0"); + } + else + { + step = atoi(val[SIMPLIFY_STEP].ToString()) + 1; + if(step >= total) + val.Remove(SIMPLIFY_STEP); + else + val[SIMPLIFY_STEP].Set(FormatInt(step)); + } + return step; +} diff --git a/uppsrc/ide/Debuggers/TypeSimplify.h b/uppsrc/ide/Debuggers/TypeSimplify.h index 6a58bb9cb..c31e986ce 100644 --- a/uppsrc/ide/Debuggers/TypeSimplify.h +++ b/uppsrc/ide/Debuggers/TypeSimplify.h @@ -6,10 +6,15 @@ #define SIMPLIFY_EXPR "" #define SIMPLIFY_VALUE "" +#define SIMPLIFY_TEMPVAL "" #define SIMPLIFY_HINT "" #define SIMPLIFY_START "" #define SIMPLIFY_COUNT "" +#define SIMPLIFY_STEP "" +#define SIMPLIFY_STEPVAL "" + + #define SIMPLIFY_SIMPLE "" #define SIMPLIFY_ARRAY "" #define SIMPLIFY_MAP "" @@ -18,12 +23,15 @@ // parameters: // gdb handler to debugger object -- used mostly to inspect deeper // expRoot gdb evaluable expression of 'val' expression root. The one used to get the 'val'. -typedef bool (*TYPE_SIMPLIFIER_HANDLER)(Gdb_MI2 &gdb, MIValue &val); +typedef bool (*TYPE_SIMPLIFIER_HANDLER)(Gdb_MI2 &gdb, MIValue &val, bool deep); void RegisterSimplifier(const char *pattern, TYPE_SIMPLIFIER_HANDLER handler); TYPE_SIMPLIFIER_HANDLER GetSimplifier(String const &pattern); +// next index when stepping through containers values +int SIMPLIFIER_NEXT_INDEX(MIValue &val, int total); + #define REGISTERSIMPLIFIER(pattern, handler) \ INITBLOCK { \ RegisterSimplifier(pattern, handler); \ diff --git a/uppsrc/ide/Debuggers/UppSimplifiers.icpp b/uppsrc/ide/Debuggers/UppSimplifiers.icpp index b8af40a76..879ef72e0 100644 --- a/uppsrc/ide/Debuggers/UppSimplifiers.icpp +++ b/uppsrc/ide/Debuggers/UppSimplifiers.icpp @@ -1,7 +1,26 @@ #include "TypeSimplify.h" -static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val) +#define EVALDEEP +#define EVALDEEP_VECTOR 5 +#define EVALDEEP_ARRAY 5 +#define EVALDEEP_VECTORMAP 5 +#define EVALDEEP_ARRAYMAP 5 +#define EVALDEEP_INDEX 5 + +#define SLEN 15 +#define LLEN 2 +static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) { + union + { + char chr[16]; + char *ptr; + dword *wptr; + qword *qptr; + word v[8]; + dword w[4]; + qword q[2]; + } u; // strings are "easy", in debug mode 's' and 'len' members are provided // so we shall just look at them in tuple @@ -13,20 +32,25 @@ static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val) if(!v.IsTuple() || !unn.IsTuple()) return false; - if(unn.Find("chr") < 0) return false; - String chr = unn["chr"][SIMPLIFY_VALUE]; - bool isSmall = (chr[14] == 0); + String chrs = unn["chr"][SIMPLIFY_VALUE]; + memcpy(u.chr, ~chrs, 16); + + bool isSmall = (u.chr[14] == 0); String s; if(isSmall) - s = chr; + { + byte len = u.chr[SLEN]; + s = chrs.Left(len); + } else { if(unn.Find("ptr") < 0) return false; - s = unn["ptr"][SIMPLIFY_VALUE]; - + dword len = u.w[LLEN]; + s = unn["ptr"][SIMPLIFY_VALUE].ToString(); + // strip address... int i = s.Find('"'); if(i >= 0) @@ -34,10 +58,13 @@ static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val) s = s.Mid(i+1); s = s.Left(s.GetCount()-1); } + s = s.Left(len); } val[SIMPLIFY_VALUE].Set("\"" + s + "\""); val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_SIMPLE); - return true; + + // no need for further evaluation on this object + return false; } catch(...) { @@ -45,9 +72,13 @@ static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val) } } -static bool UppVectorSimplify(Gdb_MI2 &gdb, MIValue &val) +static bool UppVectorSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) { -//RLOG(val.Dump()); + // if we're just doing first scan phase, signal that we need further evaluation later +#ifdef EVALDEEP + if(!deep) + return true; +#endif try { MIValue &v = val[SIMPLIFY_VALUE]; @@ -61,31 +92,40 @@ static bool UppVectorSimplify(Gdb_MI2 &gdb, MIValue &val) // display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type // otherwise display {...} string // to do this, we shall ask gdb to evaluate the undelying vector - int count = min(5, items); - String vals = ""; - for(int i = 0; i < count; i++) + String vals; + int count = min(EVALDEEP_VECTOR, items); +//RLOG("COUNT : " << count); +#ifdef EVALDEEP + if(count) { - MIValue vi = gdb.Evaluate(vectorExpr + Format("[%d]", i)); - if(vi.IsError()) + vals = ": [ ... ]"; + String expr = vectorExpr + Format("[0]@%d", count); + MIValue vi = gdb.Evaluate(expr); +//RLOG(vi.Dump()); + if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString()) { - vals = "can't evaluate"; - break; + vals = ": [ "; + for(int i = 0; i < vi.GetCount(); i++) + vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , "; + if(count < items) + vals << "... ]"; + else + vals = vals.Left(vals.GetCount() - 2) + "]"; } - if(!vi.IsString()) - { - vals = "{...}"; - break; - } - vals = vals + ", " + vi.ToString(); } - if(vals.StartsWith(", ")) - vals = vals.Mid(2); - vals = Format("Upp::Vector with %d elements : %s", items, vals); -//RLOG("VALS = " << vals); +#else + if(count) + vals = ": [ ... ]"; +#endif + vals = Format("Upp::Vector with %d elements %s", items, vals); val[SIMPLIFY_VALUE].Set(vals); -//RLOG(v.Dump()); + // signal we've done a deep evaluation +#ifdef EVALDEEP return true; +#else + return false; +#endif } catch(...) { @@ -93,9 +133,14 @@ static bool UppVectorSimplify(Gdb_MI2 &gdb, MIValue &val) } } -static bool UppVectorMapSimplify(Gdb_MI2 &gdb, MIValue &val) +static bool UppVectorMapSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) { //RLOG(val.Dump()); + // if we're just doing first scan phase, signal that we need further evaluation later +#ifdef EVALDEEP + if(!deep) + return true; +#endif try { MIValue &v = val[SIMPLIFY_VALUE]; @@ -112,51 +157,47 @@ static bool UppVectorMapSimplify(Gdb_MI2 &gdb, MIValue &val) val.FindAdd(SIMPLIFY_COUNT, FormatInt(items)); - // display max 3 elements of map, starting from 0 and ONLY if elements evaluate to a simple type + // display max 5 elements of map, starting from 0 and ONLY if elements evaluate to a simple type // otherwise display {...} string // to do this, we shall ask gdb to evaluate the undelying vector - int count = min(3, items); - String vals = ""; - bool err = false; - bool complex = false; - for(int i = 0; i < count; i++) + String vals; + int count = min(EVALDEEP_VECTORMAP, items); +#ifdef EVALDEEP + if(count) { - MIValue vk = gdb.Evaluate(keyExpr + Format("[%d]", i)); - if(vk.IsError()) + vals = ": { ... }"; + String kExpr = keyExpr + Format("[0]@%d", count); + MIValue viKey = gdb.Evaluate(kExpr); + if(viKey.IsArray() && viKey[0][SIMPLIFY_VALUE].IsString()) { - err = true; - break; + String vExpr = valExpr + Format("[0]@%d", count); + MIValue viVal = gdb.Evaluate(vExpr); + if(viVal.IsArray() && viVal[0][SIMPLIFY_VALUE].IsString()) + { + vals = ": { "; + for(int i = 0; i < viKey.GetCount(); i++) + vals << "( " << viKey[i][SIMPLIFY_VALUE].ToString() << " , " << viVal[i][SIMPLIFY_VALUE].ToString() << " ) , "; + if(count < items) + vals << "... }"; + else + vals = vals.Left(vals.GetCount() - 2) + "}"; + } +//RLOG(vals); } - if(!vk.IsString()) - { - complex = true; - break; - } - MIValue vv = gdb.Evaluate(valExpr + Format("[%d]", i)); - if(vv.IsError()) - { - err = true; - break; - } - if(!vv.IsString()) - { - complex = true; - break; - } - vals = vals + ", (" + vk.ToString() + "," + vv.ToString() + ")"; } - - if(complex) - vals = "{...}"; - else if(err) - vals = "can't evaluate"; - if(vals.StartsWith(", ")) - vals = vals.Mid(2); - vals = Format("Upp::VectorMap with %d elements : %s", items, vals); -//RLOG("VALS = " << vals); +#else + if(count) + vals = ": { ... }"; +#endif + vals = Format("Upp::VectorMap with %d elements %s", items, vals); val[SIMPLIFY_VALUE].Set(vals); - return true; + // signal we've done a deep evaluation +#ifdef EVALDEEP + return true; +#else + return false; +#endif } catch(...) { @@ -165,9 +206,14 @@ static bool UppVectorMapSimplify(Gdb_MI2 &gdb, MIValue &val) } -static bool UppArraySimplify(Gdb_MI2 &gdb, MIValue &val) +static bool UppArraySimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) { //RLOG(val.Dump()); + // if we're just doing first scan phase, signal that we need further evaluation later +#ifdef EVALDEEP + if(!deep) + return true; +#endif try { MIValue &v = val[SIMPLIFY_VALUE]["vector"][SIMPLIFY_VALUE]; @@ -182,31 +228,46 @@ static bool UppArraySimplify(Gdb_MI2 &gdb, MIValue &val) // display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type // otherwise display {...} string // to do this, we shall ask gdb to evaluate the undelying vector - int count = min(5, items); - String vals = ""; - for(int i = 0; i < count; i++) + String vals; + int count = min(EVALDEEP_ARRAY, items); +#ifdef EVALDEEP + if(count) { - MIValue vi = gdb.Evaluate(vectorExpr + Format("[%d][0]", i)); - if(vi.IsError()) + // pre-fetch main array variable to speedup element request + gdb.MICmd("gdb-set variable $thearray=" + vectorExpr); + + // get array elements + vals = ": [ ... ]"; + String expr = "{"; + for(int i = 0; i < count; i++) + expr << Format("$thearray[%d][0]", i) << ","; + expr = expr.Left(expr.GetCount()-1) + "}"; + MIValue vi = gdb.Evaluate(expr); + + if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString()) { - vals = "can't evaluate"; - break; + vals = ": [ "; + for(int i = 0; i < vi.GetCount(); i++) + vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , "; + if(count < items) + vals << "... ]"; + else + vals = vals.Left(vals.GetCount() - 2) + "]"; } - if(!vi.IsString()) - { - vals = "{...}"; - break; - } - vals = vals + ", " + vi.ToString(); } - if(vals.StartsWith(", ")) - vals = vals.Mid(2); - vals = Format("Upp::Array with %d elements : %s", items, vals); -//RLOG("VALS = " << vals); +#else + if(count) + vals = ": [ ... ]"; +#endif + vals = Format("Upp::Array with %d elements %s", items, vals); val[SIMPLIFY_VALUE].Set(vals); -//RLOG(v.Dump()); + // signal we've done a deep evaluation +#ifdef EVALDEEP return true; +#else + return false; +#endif } catch(...) { @@ -214,9 +275,14 @@ static bool UppArraySimplify(Gdb_MI2 &gdb, MIValue &val) } } -static bool UppArrayMapSimplify(Gdb_MI2 &gdb, MIValue &val) +static bool UppArrayMapSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) { //RLOG(val.Dump()); + // if we're just doing first scan phase, signal that we need further evaluation later +#ifdef EVALDEEP + if(!deep) + return true; +#endif try { MIValue &v = val[SIMPLIFY_VALUE]; @@ -236,49 +302,51 @@ static bool UppArrayMapSimplify(Gdb_MI2 &gdb, MIValue &val) // display max 3 elements of map, starting from 0 and ONLY if elements evaluate to a simple type // otherwise display {...} string // to do this, we shall ask gdb to evaluate the undelying vector - int count = min(3, items); - String vals = ""; - bool err = false; - bool complex = false; - for(int i = 0; i < count; i++) + String vals; + int count = min(EVALDEEP_ARRAYMAP, items); +#ifdef EVALDEEP + if(count) { - MIValue vk = gdb.Evaluate(keyExpr + Format("[%d]", i)); - if(vk.IsError()) + // pre-fetch main array variable to speedup element request + gdb.MICmd("gdb-set variable $thearray=" + valExpr); + + vals = ": { ... }"; + String kExpr = keyExpr + Format("[0]@%d", count); + MIValue viKey = gdb.Evaluate(kExpr); + if(viKey.IsArray() && viKey[0][SIMPLIFY_VALUE].IsString()) { - err = true; - break; + String vExpr = "{"; + for(int i = 0; i < count; i++) + vExpr << Format("$thearray[%d][0]", i) << ","; + vExpr = vExpr.Left(vExpr.GetCount()-1) + "}"; + MIValue viVal = gdb.Evaluate(vExpr); + + if(viVal.IsArray() && viVal[0][SIMPLIFY_VALUE].IsString()) + { + vals = ": { "; + for(int i = 0; i < viKey.GetCount(); i++) + vals << "( " << viKey[i][SIMPLIFY_VALUE].ToString() << " , " << viVal[i][SIMPLIFY_VALUE].ToString() << " ) , "; + if(count < items) + vals << "... }"; + else + vals = vals.Left(vals.GetCount() - 2) + "}"; + } +//RLOG(vals); } - if(!vk.IsString()) - { - complex = true; - break; - } - MIValue vv = gdb.Evaluate(valExpr + Format("[%d][0]", i)); - if(vv.IsError()) - { - err = true; - break; - } - if(!vv.IsString()) - { - complex = true; - break; - } - vals = vals + ", (" + vk.ToString() + "," + vv.ToString() + ")"; } - - if(complex) - vals = "{...}"; - else if(err) - vals = "can't evaluate"; - if(vals.StartsWith(", ")) - vals = vals.Mid(2); - vals = Format("Upp::ArrayMap with %d elements : %s", items, vals); -//RLOG("VALS = " << vals); +#else + if(count) + vals = ": { ... }"; +#endif + vals = Format("Upp::ArrayMap with %d elements %s", items, vals); val[SIMPLIFY_VALUE].Set(vals); + // signal we've done a deep evaluation +#ifdef EVALDEEP return true; - +#else + return false; +#endif } catch(...) { @@ -286,6 +354,65 @@ static bool UppArrayMapSimplify(Gdb_MI2 &gdb, MIValue &val) } } +static bool UppIndexSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep) +{ +//RLOG(val.Dump()); + // if we're just doing first scan phase, signal that we need further evaluation later +#ifdef EVALDEEP + if(!deep) + return true; +#endif + try + { + MIValue &keyExpr = val[SIMPLIFY_VALUE][1][SIMPLIFY_VALUE]["key"][SIMPLIFY_VALUE]; + String vectorExpr = keyExpr["vector"][SIMPLIFY_EXPR]; + int items = atoi(keyExpr["items"][SIMPLIFY_VALUE].ToString()); + + val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_ARRAY); + val.FindAdd(SIMPLIFY_START, "0"); + val.FindAdd(SIMPLIFY_COUNT, FormatInt(items)); + + // display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type + // otherwise display {...} string + // to do this, we shall ask gdb to evaluate the undelying vector + String vals; + int count = min(EVALDEEP_INDEX, items); +#ifdef EVALDEEP + if(count) + { + vals = ": [ ... ]"; + String expr = vectorExpr << Format("[0]@%d", count); + MIValue vi = gdb.Evaluate(expr); + if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString()) + { + vals = ": [ "; + for(int i = 0; i < vi.GetCount(); i++) + vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , "; + if(count < items) + vals << "... ]"; + else + vals = vals.Left(vals.GetCount() - 2) + "]"; + } + } +#else + if(count) + vals = ": [ ... ]"; +#endif + vals = Format("Upp::Index with %d elements %s", items, vals); + val[SIMPLIFY_VALUE].Set(vals); + + // signal we've done a deep evaluation +#ifdef EVALDEEP + return true; +#else + return false; +#endif + } + catch(...) + { + return false; + } +} // Register the simplifiers REGISTERSIMPLIFIER(">>" , UppStringSimplify); @@ -293,5 +420,5 @@ REGISTERSIMPLIFIER("