Commit 376f58df authored by hjk's avatar hjk

Debugger: Fix QObject/property dumper

Change-Id: Ied68fd07e077a185223a68cc504fff5a5b9391ac
Reviewed-by: default avatarFriedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent 9528230b
......@@ -274,6 +274,9 @@ class DumperBase:
# This keeps canonical forms of the typenames, without array indices etc.
self.cachedFormats = {}
self.knownQObjectTypes = set()
self.knownNonQObjectTypes = set()
def stripForFormat(self, typeName):
if typeName in self.cachedFormats:
......@@ -769,6 +772,8 @@ class DumperBase:
self.putItem(value.dereference())
def putQObjectNameValue(self, value):
if str(value.type) in self.knownNonQObjectTypes:
return
try:
intSize = self.intSize()
ptrSize = self.ptrSize()
......@@ -817,14 +822,50 @@ class DumperBase:
if size == 0:
return False
str = self.readMemory(data, 2 * size)
self.putValue(str, Hex4EncodedLittleEndian, 1)
raw = self.readMemory(data, 2 * size)
self.putValue(raw, Hex4EncodedLittleEndian, 1)
return True
except:
self.knownNonQObjectTypes.insert(str(value.type))
pass
def staticQObjectPropertyNames(self, metaobject):
properties = []
dd = metaobject["d"]
data = self.dereferenceValue(dd["data"])
byteArrayDataType = self.lookupType(self.qtNamespace() + "QByteArrayData")
byteArrayDataSize = byteArrayDataType.sizeof
sd = self.dereferenceValue(dd["stringdata"])
propertyCount = self.extractInt(data + 24)
propertyData = self.extractInt(data + 28)
for i in range(propertyCount):
x = data + (propertyData + 3 * i) * 4
literal = sd + self.extractInt(x) * byteArrayDataSize
ldata, lsize, lalloc = self.byteArrayDataHelper(literal)
properties.append(self.readCArray(ldata, lsize))
return properties
# This is called is when a QObject derived class is expanded
def putQObjectGuts(self, qobject):
smo = self.childWithName(qobject, "staticMetaObject")
if smo is None:
return
with SubItem(self, "[properties]"):
propertyNames = self.staticQObjectPropertyNames(smo)
propertyCount = len(propertyNames)
self.putItemCount(propertyCount)
self.putNumChild(propertyCount)
with Children(self):
for i in range(propertyCount):
name = propertyNames[i]
self.putCallItem(name, qobject, "property", '"' + name + '"')
def isKnownMovableType(self, type):
if type in (
"QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
......
......@@ -717,12 +717,11 @@ class Dumper(DumperBase):
def call(self, value, func, *args):
return self.call2(value, func, args)
def hasChildWithName(self, value, name):
def childWithName(self, value, name):
try:
value[name]
return True
return value[name]
except:
return False
return None
def makeValue(self, type, init):
type = "::" + stripClassTag(str(type));
......@@ -961,6 +960,11 @@ class Dumper(DumperBase):
return toInteger(value.cast(self.voidPtrType()))
def isQObject(self, value):
typeName = str(value.type)
if typeName in self.knownQObjectTypes:
return True
if typeName in self.knownNonQObjectTypes:
return False
try:
vtable = self.dereference(toInteger(value.address)) # + ptrSize
if vtable & 0x3: # This is not a pointer.
......@@ -972,9 +976,15 @@ class Dumper(DumperBase):
s = gdb.execute("info symbol 0x%x" % metaObjectEntry, to_string=True)
#warn("S: %s " % s)
#return s.find("::metaObject() const") > 0
return s.find("::metaObject() const") > 0 or s.find("10metaObjectEv") > 0
#return str(metaObjectEntry).find("::metaObject() const") > 0
if s.find("::metaObject() const") > 0 or s.find("10metaObjectEv") > 0:
self.knownQObjectTypes.add(typeName)
return True
else:
self.knownNonQObjectTypes.add(typeName)
return False
except:
self.knownNonQObjectTypes.add(typeName)
return False
def isQObject_B(self, value):
......@@ -1398,7 +1408,8 @@ class Dumper(DumperBase):
#warn("INAME: %s " % self.currentIName)
#warn("INAMES: %s " % self.expandedINames)
#warn("EXPANDED: %s " % (self.currentIName in self.expandedINames))
if self.isQObject(value):
isQObject = self.isQObject(value)
if isQObject:
self.putQObjectNameValue(value) # Is this too expensive?
self.putType(typeName)
self.putEmptyValue()
......@@ -1408,6 +1419,9 @@ class Dumper(DumperBase):
innerType = None
with Children(self, 1, childType=innerType):
self.putFields(value)
if isQObject:
self.putQObjectGuts(value)
def putPlainChildren(self, value):
self.putEmptyValue(-99)
......@@ -1423,6 +1437,41 @@ class Dumper(DumperBase):
return bytesToString(binascii.hexlify(mem.tobytes()))
return binascii.hexlify(mem)
def readCArray(self, base, size):
inferior = self.selectedInferior()
if sys.version_info[0] >= 3:
mem = bytes()
for i in range(size):
char = inferior.read_memory(base + i, 1)[0]
if not char:
break
mem += char
return mem.decode("utf8")
else:
mem = ""
for i in range(size):
char = inferior.read_memory(base + i, 1)[0]
if not char:
break
mem += char
return mem
def readCString(self, base):
inferior = self.selectedInferior()
mem = ""
while True:
char = inferior.read_memory(base, 1)[0]
if not char:
break
mem += char
base += 1
#if sys.version_info[0] >= 3:
# return mem.tobytes()
return mem
#if sys.version_info[0] >= 3:
# return bytesToString(binascii.hexlify(mem.tobytes()))
#return binascii.hexlify(mem)
def putFields(self, value, dumpBase = True):
fields = value.type.fields()
......@@ -1638,15 +1687,15 @@ class Dumper(DumperBase):
# FIXME: This only works when call from inside a Qt function frame.
namespace = ""
try:
str = gdb.execute("ptype QString::Null", to_string=True)
out = gdb.execute("ptype QString::Null", to_string=True)
# The result looks like:
# "type = const struct myns::QString::Null {"
# " <no data fields>"
# "}"
pos1 = str.find("struct") + 7
pos2 = str.find("QString::Null")
pos1 = out.find("struct") + 7
pos2 = out.find("QString::Null")
if pos1 > -1 and pos2 > -1:
namespace = str[pos1:pos2]
namespace = out[pos1:pos2]
self.cachedQtNamespace = namespace
self.ns = lambda: self.cachedQtNamespace
except:
......
......@@ -378,8 +378,9 @@ class Dumper(DumperBase):
typeClass = typeobj.GetTypeClass()
return typeClass == lldb.eTypeClassBuiltin
def hasChildWithName(self, value, name):
return value.GetChildMemberWithName(name).IsValid()
def childWithName(self, value, name):
child = value.GetChildMemberWithName(name)
return child if child.IsValid() else None
def childAt(self, value, index):
return value.GetChildAtIndex(index)
......
This diff is collapsed.
......@@ -288,8 +288,9 @@ def stdTreeIteratorHelper(d, value):
nodeType = d.lookupType(nodeTypeName)
data = node.cast(nodeType)["_M_value_field"]
with Children(d):
if d.hasChildWithName(data, "first"):
d.putSubItem("first", data["first"])
first = d.childWithName(data, "first")
if first:
d.putSubItem("first", first)
d.putSubItem("second", data["second"])
else:
d.putSubItem("value", data)
......
......@@ -2287,28 +2287,37 @@ void tst_Dumpers::dumper_data()
" Q_PROPERTY(QString myProp1 READ myProp1 WRITE setMyProp1)\n"
" QString myProp1() const { return m_myProp1; }\n"
" Q_SLOT void setMyProp1(const QString&mt) { m_myProp1 = mt; }\n"
" Q_PROPERTY(QString myProp2 READ myProp2 WRITE setMyProp2)\n"
" QString myProp2() const { return m_myProp2; }\n"
" Q_SLOT void setMyProp2(const QString&mt) { m_myProp2 = mt; }\n"
" Q_PROPERTY(QByteArray myProp2 READ myProp2 WRITE setMyProp2)\n"
" QByteArray myProp2() const { return m_myProp2; }\n"
" Q_SLOT void setMyProp2(const QByteArray&mt) { m_myProp2 = mt; }\n"
" Q_PROPERTY(long myProp3 READ myProp3)\n"
" long myProp3() const { return 54; }\n"
" Q_PROPERTY(int myProp4 READ myProp4)\n"
" int myProp4() const { return 44; }\n"
" public:\n"
" Ui *m_ui;\n"
" QString m_myProp1;\n"
" QString m_myProp2;\n"
" QByteArray m_myProp2;\n"
" };\n"
"} // namespace Bar\n"
"} // namespace Names\n"
"#include <main.moc>\n",
""
"QApplication app(argc, argv);\n"
"Q_UNUSED(app)\n"
"Names::Bar::TestObject test;\n"
"test.setMyProp1(\"HELLO\");\n"
"test.setMyProp2(\"WORLD\");\n"
"test.setMyProp1(\"Hello\");\n"
"test.setMyProp2(\"World\");\n"
"QString s = test.myProp1();\n"
"s += test.myProp2();\n")
"s += QString::fromLatin1(test.myProp2());\n"
"unused(&app, &test, &s);\n")
% GuiProfile()
% Check("s", "\"HELLOWORLD\"", "@QString")
% Check("test", "", "Names::Bar::TestObject");
% Check("s", "\"HelloWorld\"", "@QString")
% Check("test", "", "Names::Bar::TestObject")
% Check("test.[properties]", "<4 items>", "")
% Check("test.[properties].myProp1", "\"Hello\"", "@QVariant (QString)")
% Check("test.[properties].myProp2", "\"World\"", "@QVariant (QByteArray)")
% Check("test.[properties].myProp3", "54", "@QVariant (long)")
% Check("test.[properties].myProp4", "44", "@QVariant (int)");
QTest::newRow("QObject3")
<< Data("#include <QWidget>\n"
......
......@@ -1802,6 +1802,12 @@ namespace qobject {
QString myProp2() const { return m_myProp2; }
Q_SLOT void setMyProp2(const QString&mt) { m_myProp2 = mt; }
Q_PROPERTY(long myProp3 READ myProp3)
long myProp3() const { return 54; }
Q_PROPERTY(long myProp4 READ myProp4)
long myProp4() const { return 44; }
public:
Ui *m_ui;
QString m_myProp1;
......@@ -1817,6 +1823,7 @@ namespace qobject {
Names::Bar::TestObject test;
test.setMyProp1("HELLO");
test.setMyProp2("WORLD");
test.setObjectName("An object");
QString s = test.myProp1();
s += test.myProp2();
BREAK_HERE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment