(De)serialization of Comments
authorChristoph Egger <Christoph.Egger@fau.de>
Tue, 24 Mar 2015 15:45:27 +0000 (16:45 +0100)
committerChristoph Egger <Christoph.Egger@fau.de>
Tue, 24 Mar 2015 15:46:53 +0000 (16:46 +0100)
serialization and deserialization of global comments. Local comments
need to go within the function and are not yet implemented. Also
includes several tests

CMakeLists.txt
src/core/Comment.cxx
src/core/InformationManager.cxx
src/core/tests/CommentTest.cxx [new file with mode: 0644]
src/gui/widgets/BasicBlockWidget.cxx
testdata/core/Comment/invalid/400580.xml [new file with mode: 0644]
testdata/core/Comment/invalid/additionalAttribute.xml [new file with mode: 0644]
testdata/core/Comment/invalid/noAddress.xml [new file with mode: 0644]
testdata/core/Comment/invalid/noText.xml [new file with mode: 0644]
testdata/core/Comment/invalid/wrongAttribute.xml [new file with mode: 0644]
testdata/core/Comment/valid/400580.xml [new file with mode: 0644]

index e72d4396d2e4e1e108b26bc68ad338afdababbae..cbc597f0f6ed7ddc7f4bbeb7dc99cae656476d3b 100644 (file)
@@ -128,6 +128,7 @@ set(fridatest_SOURCES
   src/test.cxx
   src/core/tests/BasicBlockTest.cxx
   src/core/tests/FunctionTest.cxx
+  src/core/tests/CommentTest.cxx
   ${frida_SOURCES}
   )
 
index d6fedfbf00331b7c026342e693d9219fe6e21b4f..e864ef673cb71abc182a0b51cbc1d2aa8528a667 100644 (file)
@@ -26,3 +26,70 @@ uint64_t Comment::getAddress() {
 Function* Comment::getLocation() {
        return location;
 }
+
+void Comment::serialize(QXmlStreamWriter& stream) {
+       stream.writeStartElement("comment");
+
+       stream.writeTextElement("address", QString::number(address, 16));
+       stream.writeTextElement("text", text.c_str());
+
+       stream.writeEndElement(); // "comment"
+}
+
+Comment* Comment::deserialize(QXmlStreamReader& stream, InformationManager* manager, Function* function) {
+       Q_ASSERT(stream.name() == "comment");
+
+       QString text;
+       uint64_t address = 0;
+       Comment* comment;
+
+       while (QXmlStreamReader::NoToken != stream.readNext()) {
+               while (QXmlStreamReader::Characters == stream.tokenType() &&
+                      stream.isWhitespace())
+                       stream.readNext();
+               if (QXmlStreamReader::EndElement == stream.tokenType())
+                       break;
+
+               if(QXmlStreamReader::StartElement != stream.tokenType())
+                       return NULL;
+
+               if (stream.name() == "text") {
+                       stream.readNext();
+                       if (QXmlStreamReader::Characters != stream.tokenType())
+                               return NULL;
+
+                       text = stream.text().toString();
+                       stream.readNext();
+
+                       if(QXmlStreamReader::EndElement != stream.tokenType())
+                               return NULL;
+               }
+               if (stream.name() == "address") {
+                       stream.readNext();
+                       if (QXmlStreamReader::Characters != stream.tokenType())
+                               return NULL;
+
+                       address = stream.text().toULongLong(NULL, 16);
+                       stream.readNext();
+
+                       if(QXmlStreamReader::EndElement != stream.tokenType())
+                               return NULL;
+               }
+       }
+
+       if (address == 0 or text == "")
+               return NULL;
+
+       if (function)
+               comment = manager->newLocalComment(address, function);
+       else
+               comment = manager->newGlobalComment(address);
+
+       comment->text = text.toStdString();
+
+       assert(stream.name() == "comment");
+
+       manager->finishComment(comment);
+       return comment;
+}
+
index 6179f5f1ff2d83c5287ed07f629674907da15139..485aacd9868a954b350d5c43a958de71a72c8b75 100644 (file)
@@ -73,10 +73,21 @@ void InformationManager::load(const std::string& filename) {
                file.open(QIODevice::ReadOnly);
 
                if(info.name != "binary") {
-                       QXmlStreamReader reader(&file);
-                       assert(QXmlStreamReader::StartDocument == reader.readNext());
-                       assert(QXmlStreamReader::StartElement == reader.readNext());
-                       Function::deserialize(reader, this);
+                       if (info.name.startsWith("comment:")) {
+                               QXmlStreamReader reader(&file);
+                               auto starttoken = reader.readNext();
+                               auto elementtoken = reader.readNext();
+                               assert(QXmlStreamReader::StartDocument == starttoken);
+                               assert(QXmlStreamReader::StartElement == elementtoken);
+                               Comment::deserialize(reader, this);
+                       } else {
+                               QXmlStreamReader reader(&file);
+                               auto starttoken = reader.readNext();
+                               auto elementtoken = reader.readNext();
+                               assert(QXmlStreamReader::StartDocument == starttoken);
+                               assert(QXmlStreamReader::StartElement == elementtoken);
+                               Function::deserialize(reader, this);
+                       }
                }
                file.close();
        }
@@ -117,6 +128,23 @@ void InformationManager::save(const std::string& filename) {
                stream.writeEndDocument();
                outZipFile.close();
        }
+       for (auto commentpair : comments) {
+               Comment* comment = commentpair.second;
+               if (!comment->isLocal()) {
+                       QuaZipNewInfo zipinfo(QString("comment:%1").arg(comment->getAddress(), 0, 16));
+                       zipinfo.setPermissions(static_cast<QFile::Permissions>(0x6444));
+                       outZipFile.open(QIODevice::WriteOnly, zipinfo);
+                       QXmlStreamWriter stream(&outZipFile);
+                       stream.setAutoFormatting(true);
+                       stream.setAutoFormattingIndent(-1);
+                       stream.writeStartDocument();
+
+                       comment->serialize(stream);
+
+                       stream.writeEndDocument();
+                       outZipFile.close();
+               }
+       }
 
        zip.close();
 }
diff --git a/src/core/tests/CommentTest.cxx b/src/core/tests/CommentTest.cxx
new file mode 100644 (file)
index 0000000..870402a
--- /dev/null
@@ -0,0 +1,79 @@
+#include <gtest/gtest.h>
+
+#include "gui/qt.hxx"
+#include "core/InformationManager.hxx"
+#include "core/BasicBlock.hxx"
+#include "core/Comment.hxx"
+#include <iostream>
+
+extern char * TEST_DATA_DIRECTORY;
+
+TEST(CommentTest, deserializeValidInstance) {
+       InformationManager manager;
+       QDir directory(QString("%1/%2").arg(TEST_DATA_DIRECTORY,"/core/Comment/valid/"));
+       QStringList filters;
+       filters << "*.xml";
+       directory.setNameFilters(filters);
+
+       for (auto fileinfo : directory.entryInfoList()) {
+               if (false == fileinfo.isFile())
+                       continue;
+
+               QFile file(fileinfo.absoluteFilePath());
+               file.open(QFile::ReadOnly | QFile::Text);
+               QXmlStreamReader reader(&file);
+               reader.readNextStartElement();
+
+               Comment* c = Comment::deserialize(reader, &manager);
+               ASSERT_NE((void*)NULL, (void*)c);
+               EXPECT_EQ(fileinfo.baseName().toULongLong(NULL, 16), c->getAddress());
+       }
+}
+
+TEST(CommentTest, deserializeInvalidInstance) {
+       InformationManager manager;
+       QDir directory(QString("%1/%2").arg(TEST_DATA_DIRECTORY,"/core/Comment/invalid/"));
+       QStringList filters;
+       filters << "*.xml";
+       directory.setNameFilters(filters);
+
+       for (auto fileinfo : directory.entryInfoList()) {
+               if (false == fileinfo.isFile())
+                       continue;
+
+               QFile file(fileinfo.absoluteFilePath());
+               file.open(QFile::ReadOnly | QFile::Text);
+               QXmlStreamReader reader(&file);
+               reader.readNextStartElement();
+
+               Comment* c = Comment::deserialize(reader, &manager);
+               ASSERT_EQ((void*)NULL, (void*)c);
+       }
+}
+
+TEST(CommentTest, serializeThenDeserializeIsIdentity) {
+       InformationManager manager;
+       Comment* c = manager.newGlobalComment(0x42234223);
+
+       for (QString text : QStringList({"αβγδ", "<test>", "&auml;"})) {
+               c->setText(text.toStdString());
+
+               QTemporaryFile tmpfile;
+
+               {
+                       tmpfile.open();
+                       QXmlStreamWriter writer(&tmpfile);
+                       c->serialize(writer);
+               }
+               tmpfile.seek(0);
+               {
+                       tmpfile.open();
+                       QXmlStreamReader reader(&tmpfile);
+                       reader.readNextStartElement();
+                       Comment* d = Comment::deserialize(reader, &manager);
+                       ASSERT_NE((void*)NULL, (void*)d);
+                       EXPECT_STREQ(c->getText().c_str(), d->getText().c_str());
+                       EXPECT_EQ(0x42234223, d->getAddress());
+               }
+       }
+}
index bdddd520f429c925fb4a446a3209867e2a13e1de..efa7ec34394ec6d2416933c99d452d1b054032fa 100644 (file)
@@ -30,7 +30,7 @@ void CustomQGraphicsTextItem::addComment(int row, bool global) {
                if (global) {
                        comment = parent->block->getManager()->newGlobalComment(address);
                } else {
-                       comment = parent->block->getManager()->newLocalComment(address, NULL);
+                       comment = parent->block->getManager()->newLocalComment(address, (Function*)0x23);
                }
                comment->setText(dialog.result().toStdString());
                parent->block->getManager()->finishComment(comment);
@@ -195,7 +195,7 @@ QString BasicBlockWidget::formatComments(Instruction* inst) {
        QString comments;
        for (Comment* c: inst->comments()) {
                comments += "<br />";
-               comments += c->getText().c_str();
+               comments += QString(c->getText().c_str()).toHtmlEscaped();
        }
        return (comments == "" ? "" : ";; ") + comments.trimmed();
 }
diff --git a/testdata/core/Comment/invalid/400580.xml b/testdata/core/Comment/invalid/400580.xml
new file mode 100644 (file)
index 0000000..0a4daee
--- /dev/null
@@ -0,0 +1,4 @@
+<comment>
+       <adress>400580</adress>
+       <text>&lt;test&gt; ist auch ein test!! &amp;wasaucheimmer;</text>
+</comment>
diff --git a/testdata/core/Comment/invalid/additionalAttribute.xml b/testdata/core/Comment/invalid/additionalAttribute.xml
new file mode 100644 (file)
index 0000000..1162e70
--- /dev/null
@@ -0,0 +1,5 @@
+<comment>
+       <address>400580</address>
+       <text>&lt;test&gt; ist auch ein test!! &amp;wasaucheimmer;</text>
+       <cow>SuperCowPowers!</cow>
+</comment>
diff --git a/testdata/core/Comment/invalid/noAddress.xml b/testdata/core/Comment/invalid/noAddress.xml
new file mode 100644 (file)
index 0000000..43469a2
--- /dev/null
@@ -0,0 +1,3 @@
+<comment>
+       <text>&lt;test&gt; ist auch ein test!! &amp;wasaucheimmer;</text>
+</comment>
diff --git a/testdata/core/Comment/invalid/noText.xml b/testdata/core/Comment/invalid/noText.xml
new file mode 100644 (file)
index 0000000..24bd89c
--- /dev/null
@@ -0,0 +1,3 @@
+<comment>
+       <address>400580</address>
+</comment>
diff --git a/testdata/core/Comment/invalid/wrongAttribute.xml b/testdata/core/Comment/invalid/wrongAttribute.xml
new file mode 100644 (file)
index 0000000..0a4daee
--- /dev/null
@@ -0,0 +1,4 @@
+<comment>
+       <adress>400580</adress>
+       <text>&lt;test&gt; ist auch ein test!! &amp;wasaucheimmer;</text>
+</comment>
diff --git a/testdata/core/Comment/valid/400580.xml b/testdata/core/Comment/valid/400580.xml
new file mode 100644 (file)
index 0000000..6f92f1c
--- /dev/null
@@ -0,0 +1,4 @@
+<comment>
+       <address>400580</address>
+       <text>&lt;test&gt; ist auch ein test!! &amp;wasaucheimmer;</text>
+</comment>