Debugger: Move struct sorting logic to generic parser

Backends only have to specify whether an object members are sortable
in principle (e.g. all structs), and some numeric 'sortgroup' value
for member items (higher values are always sorted on top).

Change-Id: I10ce94580374fed48a35f058a575a1408d6801af
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
hjk
2016-04-05 11:14:40 +02:00
parent 58c326cb73
commit e9b1e493c2
9 changed files with 37 additions and 27 deletions

View File

@@ -285,7 +285,6 @@ class Dumper(DumperBase):
self.autoDerefPointers = int(args.get("autoderef", "0")) self.autoDerefPointers = int(args.get("autoderef", "0"))
self.partialUpdate = int(args.get("partial", "0")) self.partialUpdate = int(args.get("partial", "0"))
self.fallbackQtVersion = 0x50200 self.fallbackQtVersion = 0x50200
self.sortStructMembers = bool(args.get("sortstructs", True))
#warn("NAMESPACE: '%s'" % self.qtNamespace()) #warn("NAMESPACE: '%s'" % self.qtNamespace())
#warn("EXPANDED INAMES: %s" % self.expandedINames) #warn("EXPANDED INAMES: %s" % self.expandedINames)
@@ -1144,6 +1143,7 @@ class Dumper(DumperBase):
if self.currentIName in self.expandedINames: if self.currentIName in self.expandedINames:
innerType = None innerType = None
self.put('sortable="1"')
with Children(self, 1, childType=innerType): with Children(self, 1, childType=innerType):
self.putFields(value) self.putFields(value)
if not self.showQObjectNames: if not self.showQObjectNames:
@@ -1215,15 +1215,6 @@ class Dumper(DumperBase):
def putFields(self, value, dumpBase = True): def putFields(self, value, dumpBase = True):
fields = value.type.fields() fields = value.type.fields()
if self.sortStructMembers:
def sortOrder(field):
if field.is_base_class:
return 0
if field.name and field.name.startswith("_vptr."):
return 1
return 2
fields.sort(key = lambda field: "%d%s" % (sortOrder(field), field.name))
#warn("TYPE: %s" % value.type) #warn("TYPE: %s" % value.type)
#warn("FIELDS: %s" % fields) #warn("FIELDS: %s" % fields)
baseNumber = 0 baseNumber = 0
@@ -1257,6 +1248,7 @@ class Dumper(DumperBase):
# int (**)(void) # int (**)(void)
n = 100 n = 100
self.putType(" ") self.putType(" ")
self.put('sortgroup="1"')
self.putValue(value[field.name]) self.putValue(value[field.name])
self.putNumChild(n) self.putNumChild(n)
if self.isExpanded(): if self.isExpanded():
@@ -1280,6 +1272,7 @@ class Dumper(DumperBase):
baseNumber += 1 baseNumber += 1
with UnnamedSubItem(self, "@%d" % baseNumber): with UnnamedSubItem(self, "@%d" % baseNumber):
baseValue = value.cast(field.type) baseValue = value.cast(field.type)
self.put('sortgroup="2"')
self.putBaseClassName(field.name) self.putBaseClassName(field.name)
self.putAddress(baseValue.address) self.putAddress(baseValue.address)
self.putItem(baseValue, False) self.putItem(baseValue, False)

View File

@@ -1092,6 +1092,7 @@ class Dumper(DumperBase):
self.putQObjectNameValue(value) self.putQObjectNameValue(value)
if self.currentIName in self.expandedINames: if self.currentIName in self.expandedINames:
self.put('sortable="1"')
with Children(self): with Children(self):
self.putFields(value) self.putFields(value)
if not self.showQObjectNames: if not self.showQObjectNames:
@@ -1134,8 +1135,6 @@ class Dumper(DumperBase):
baseObject = value.Cast(baseClass) baseObject = value.Cast(baseClass)
baseObjects.append(ChildItem(baseClass.GetName(), baseObject)) baseObjects.append(ChildItem(baseClass.GetName(), baseObject))
if self.sortStructMembers:
baseObjects.sort(key = lambda baseObject: str(baseObject.name))
for i in xrange(len(baseObjects)): for i in xrange(len(baseObjects)):
baseObject = baseObjects[i] baseObject = baseObjects[i]
with UnnamedSubItem(self, "@%d" % (i + 1)): with UnnamedSubItem(self, "@%d" % (i + 1)):
@@ -1147,8 +1146,6 @@ class Dumper(DumperBase):
if memberCount > 10000: if memberCount > 10000:
memberCount = 10000 memberCount = 10000
children = [value.GetChildAtIndex(memberBase + i) for i in xrange(memberCount)] children = [value.GetChildAtIndex(memberBase + i) for i in xrange(memberCount)]
if self.sortStructMembers:
children.sort(key = lambda child: str(child.GetName()))
for child in children: for child in children:
# Only needed in the QVariant4 test. # Only needed in the QVariant4 test.
if int(child.GetLoadAddress()) == 0xffffffffffffffff: if int(child.GetLoadAddress()) == 0xffffffffffffffff:
@@ -1169,7 +1166,6 @@ class Dumper(DumperBase):
self.expandedINames = set(args.get('expanded', [])) self.expandedINames = set(args.get('expanded', []))
self.autoDerefPointers = int(args.get('autoderef', '0')) self.autoDerefPointers = int(args.get('autoderef', '0'))
self.sortStructMembers = bool(args.get('sortstructs', True));
self.useDynamicType = int(args.get('dyntype', '0')) self.useDynamicType = int(args.get('dyntype', '0'))
self.useFancy = int(args.get('fancy', '0')) self.useFancy = int(args.get('fancy', '0'))
self.passExceptions = int(args.get('passexceptions', '0')) self.passExceptions = int(args.get('passexceptions', '0'))

View File

@@ -2000,10 +2000,12 @@ void DebuggerEngine::updateLocalsView(const GdbMi &all)
} }
} }
const bool sortStructMembers = boolSetting(SortStructMembers);
GdbMi data = all["data"]; GdbMi data = all["data"];
foreach (const GdbMi &child, data.children()) { foreach (const GdbMi &child, data.children()) {
WatchItem *item = new WatchItem; WatchItem *item = new WatchItem;
item->parse(child); item->parse(child, sortStructMembers);
const TypeInfo ti = d->m_typeInfoCache.value(item->type); const TypeInfo ti = d->m_typeInfoCache.value(item->type);
if (ti.size && !item->size) if (ti.size && !item->size)
item->size = ti.size; item->size = ti.size;

View File

@@ -4596,7 +4596,6 @@ void GdbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("resultvarname", m_resultVarName); cmd.arg("resultvarname", m_resultVarName);
cmd.arg("partialvar", params.partialVariable); cmd.arg("partialvar", params.partialVariable);
cmd.arg("sortstructs", boolSetting(SortStructMembers));
cmd.callback = CB(handleFetchVariables); cmd.callback = CB(handleFetchVariables);
runCommand(cmd); runCommand(cmd);

View File

@@ -793,7 +793,6 @@ void LldbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("autoderef", boolSetting(AutoDerefPointers)); cmd.arg("autoderef", boolSetting(AutoDerefPointers));
cmd.arg("dyntype", boolSetting(UseDynamicType)); cmd.arg("dyntype", boolSetting(UseDynamicType));
cmd.arg("partialvar", params.partialVariable); cmd.arg("partialvar", params.partialVariable);
cmd.arg("sortstructs", boolSetting(SortStructMembers));
cmd.arg("qobjectnames", boolSetting(ShowQObjectNames)); cmd.arg("qobjectnames", boolSetting(ShowQObjectNames));
StackFrame frame = stackHandler()->currentFrame(); StackFrame frame = stackHandler()->currentFrame();

View File

@@ -514,9 +514,10 @@ void PdbEngine::refreshLocals(const GdbMi &vars)
WatchHandler *handler = watchHandler(); WatchHandler *handler = watchHandler();
handler->resetValueCache(); handler->resetValueCache();
const bool sortStructMembers = boolSetting(SortStructMembers);
foreach (const GdbMi &child, vars.children()) { foreach (const GdbMi &child, vars.children()) {
WatchItem *item = new WatchItem; WatchItem *item = new WatchItem;
item->parse(child); item->parse(child, sortStructMembers);
handler->insertItem(item); handler->insertItem(item);
} }

View File

@@ -118,6 +118,7 @@ WatchItem::WatchItem() :
bitsize(0), bitsize(0),
elided(0), elided(0),
arrayIndex(-1), arrayIndex(-1),
sortGroup(0),
wantsChildren(false), wantsChildren(false),
valueEnabled(true), valueEnabled(true),
valueEditable(true), valueEditable(true),
@@ -380,7 +381,18 @@ static void decodeArrayData(WatchItem *item, const QByteArray &rawData,
qDebug() << "ENCODING ERROR: " << encoding.toString(); qDebug() << "ENCODING ERROR: " << encoding.toString();
} }
void WatchItem::parseHelper(const GdbMi &input) static bool sortByName(const Utils::TreeItem *a, const Utils::TreeItem *b)
{
auto aa = static_cast<const WatchItem *>(a);
auto bb = static_cast<const WatchItem *>(b);
if (aa->sortGroup != bb->sortGroup)
return aa->sortGroup > bb->sortGroup;
return aa->name < bb->name;
}
void WatchItem::parseHelper(const GdbMi &input, bool maySort)
{ {
setChildrenUnneeded(); setChildrenUnneeded();
@@ -437,6 +449,10 @@ void WatchItem::parseHelper(const GdbMi &input)
if (mi.isValid()) if (mi.isValid())
exp = mi.data(); exp = mi.data();
mi = input["sortgroup"];
if (mi.isValid())
sortGroup = mi.toInt();
mi = input["valueenabled"]; mi = input["valueenabled"];
if (mi.data() == "true") if (mi.data() == "true")
valueEnabled = true; valueEnabled = true;
@@ -500,14 +516,17 @@ void WatchItem::parseHelper(const GdbMi &input)
QByteArray key = subinput["key"].data(); QByteArray key = subinput["key"].data();
if (!key.isEmpty()) if (!key.isEmpty())
child->name = decodeData(key, subinput["keyencoded"].data()); child->name = decodeData(key, subinput["keyencoded"].data());
child->parseHelper(subinput); child->parseHelper(subinput, maySort);
appendChild(child); appendChild(child);
} }
if (maySort && input["sortable"].toInt())
sortChildren(&sortByName);
} }
} }
} }
void WatchItem::parse(const GdbMi &data) void WatchItem::parse(const GdbMi &data, bool maySort)
{ {
iname = data["iname"].data(); iname = data["iname"].data();
@@ -517,7 +536,7 @@ void WatchItem::parse(const GdbMi &data)
else else
name = QString::fromLatin1(data["name"].data()); name = QString::fromLatin1(data["name"].data());
parseHelper(data); parseHelper(data, maySort);
if (wname.isValid()) if (wname.isValid())
exp = name.toUtf8(); exp = name.toUtf8();

View File

@@ -45,7 +45,7 @@ class WatchItem : public Utils::TreeItem
public: public:
WatchItem(); WatchItem();
void parse(const GdbMi &input); void parse(const GdbMi &input, bool maySort);
bool isLocal() const; bool isLocal() const;
bool isWatcher() const; bool isWatcher() const;
@@ -117,13 +117,14 @@ public:
uint bitsize; // Size in case of bit fields uint bitsize; // Size in case of bit fields
int elided; // Full size if value was cut off, -1 if cut on unknown size, 0 otherwise int elided; // Full size if value was cut off, -1 if cut on unknown size, 0 otherwise
int arrayIndex; // -1 if not an array member int arrayIndex; // -1 if not an array member
uchar sortGroup; // 0 - ordinary member, 1 - vptr, 2 - base class
bool wantsChildren; bool wantsChildren;
bool valueEnabled; // Value will be enabled or not bool valueEnabled; // Value will be enabled or not
bool valueEditable; // Value will be editable bool valueEditable; // Value will be editable
bool outdated; // \internal item is to be removed. bool outdated; // \internal item is to be removed.
private: private:
void parseHelper(const GdbMi &input); void parseHelper(const GdbMi &input, bool maySort);
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::WatchHandler) Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::WatchHandler)
}; };

View File

@@ -1296,7 +1296,7 @@ void tst_Dumpers::dumper()
"python theDumper.setupDumpers()\n" "python theDumper.setupDumpers()\n"
"run " + nograb + "\n" "run " + nograb + "\n"
"python theDumper.fetchVariables({" "python theDumper.fetchVariables({"
"'token':2,'fancy':1,'forcens':1,'sortstructs':1," "'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1," "'autoderef':1,'dyntype':1,'passexceptions':1,"
"'qobjectnames':1," "'qobjectnames':1,"
"'expanded':[" + expandedq + "]})\n"; "'expanded':[" + expandedq + "]})\n";
@@ -1435,7 +1435,7 @@ void tst_Dumpers::dumper()
context.boostVersion = child["value"].toInt(); context.boostVersion = child["value"].toInt();
else { else {
WatchItem *item = new WatchItem; WatchItem *item = new WatchItem;
item->parse(child); item->parse(child, true);
local.appendChild(item); local.appendChild(item);
} }
} }