[lldb] Correctly escape newlines and backslashes in the JSON serializer (#248)
JSON serializer fails to escape newlines and backslashes. Let's fix that.
diff --git a/lldb.xcodeproj/project.pbxproj b/lldb.xcodeproj/project.pbxproj
index c86a3c6..661174e 100644
--- a/lldb.xcodeproj/project.pbxproj
+++ b/lldb.xcodeproj/project.pbxproj
@@ -849,6 +849,7 @@
8C26C4261C3EA5F90031DF7C /* ThreadSanitizerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C26C4241C3EA4340031DF7C /* ThreadSanitizerRuntime.cpp */; };
8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */; };
8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */; };
+ 8C3BD9A01EF5D1FF0016C343 /* JSONTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C3BD99F1EF5D1B50016C343 /* JSONTest.cpp */; };
8C3BD9961EF45DA50016C343 /* MainThreadCheckerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */; };
8CCB017E19BA28A80009FD44 /* ThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */; };
8CCB018219BA4E270009FD44 /* SBThreadCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -2839,6 +2840,7 @@
8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = "<group>"; };
8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = "<group>"; };
8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = "<group>"; };
+ 8C3BD99F1EF5D1B50016C343 /* JSONTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSONTest.cpp; sourceTree = "<group>"; };
8C3BD9931EF45D9B0016C343 /* MainThreadCheckerRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainThreadCheckerRuntime.h; sourceTree = "<group>"; };
8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MainThreadCheckerRuntime.cpp; sourceTree = "<group>"; };
8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadCollection.cpp; path = source/Target/ThreadCollection.cpp; sourceTree = "<group>"; };
@@ -3559,6 +3561,7 @@
23CB15011D66CD8400EDDDE1 /* ModuleCacheTest.cpp */,
2321F9441BDD346100BA9A93 /* StringExtractorTest.cpp */,
2321F9451BDD346100BA9A93 /* TaskPoolTest.cpp */,
+ 8C3BD99F1EF5D1B50016C343 /* JSONTest.cpp */,
2321F9461BDD346100BA9A93 /* UriParserTest.cpp */,
);
path = Utility;
@@ -7500,6 +7503,7 @@
23CB15341D66DA9300EDDDE1 /* CPlusPlusLanguageTest.cpp in Sources */,
23150FD01DB93BDB00EA5AB6 /* Testx86AssemblyInspectionEngine.cpp in Sources */,
23CB15351D66DA9300EDDDE1 /* UriParserTest.cpp in Sources */,
+ 8C3BD9A01EF5D1FF0016C343 /* JSONTest.cpp in Sources */,
23CB15361D66DA9300EDDDE1 /* FileSpecTest.cpp in Sources */,
23CB15371D66DA9300EDDDE1 /* PythonTestSuite.cpp in Sources */,
23E2E5321D903832006F38BB /* (null) in Sources */,
diff --git a/source/Utility/JSON.cpp b/source/Utility/JSON.cpp
index 5b809c5..f9c0ac9 100644
--- a/source/Utility/JSON.cpp
+++ b/source/Utility/JSON.cpp
@@ -17,7 +17,7 @@
using namespace lldb_private;
std::string JSONString::json_string_quote_metachars(const std::string &s) {
- if (s.find('"') == std::string::npos)
+ if (s.find_first_of("\\\n\"") == std::string::npos)
return s;
std::string output;
@@ -25,8 +25,9 @@
const char *s_chars = s.c_str();
for (size_t i = 0; i < s_size; i++) {
unsigned char ch = *(s_chars + i);
- if (ch == '"') {
+ if (ch == '"' || ch == '\\' || ch == '\n') {
output.push_back('\\');
+ if (ch == '\n') ch = 'n';
}
output.push_back(ch);
}
diff --git a/unittests/Utility/CMakeLists.txt b/unittests/Utility/CMakeLists.txt
index 0594434..2c53adf 100644
--- a/unittests/Utility/CMakeLists.txt
+++ b/unittests/Utility/CMakeLists.txt
@@ -1,4 +1,5 @@
add_lldb_unittest(UtilityTests
+ JSONTest.cpp
ModuleCacheTest.cpp
StringExtractorTest.cpp
TaskPoolTest.cpp
diff --git a/unittests/Utility/JSONTest.cpp b/unittests/Utility/JSONTest.cpp
new file mode 100644
index 0000000..d75770b
--- /dev/null
+++ b/unittests/Utility/JSONTest.cpp
@@ -0,0 +1,26 @@
+#include "gtest/gtest.h"
+
+#include "lldb/Core/StreamString.h"
+#include "lldb/Utility/JSON.h"
+
+using namespace lldb_private;
+
+TEST(JSONTest, Dictionary) {
+ JSONObject o;
+ o.SetObject("key", std::make_shared<JSONString>("value"));
+
+ StreamString stream;
+ o.Write(stream);
+
+ ASSERT_EQ(stream.GetString(), R"({"key":"value"})");
+}
+
+TEST(JSONTest, Newlines) {
+ JSONObject o;
+ o.SetObject("key", std::make_shared<JSONString>("hello\nworld"));
+
+ StreamString stream;
+ o.Write(stream);
+
+ ASSERT_EQ(stream.GetString(), R"({"key":"hello\nworld"})");
+}