jkorous created this revision.
jkorous added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, MaskRay, ioeric, ilya-biryukov, mgorny.

This is a self-contained pair of utility functions for the XPC transport layer.

It's not dependent on but following the refactoring patch:
https://reviews.llvm.org/D48559


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D48560

Files:
  clangd/xpc/CMakeLists.txt
  clangd/xpc/XPCJSONConversionTests.cpp
  xpc/XPCJSONConversions.cpp
  xpc/XPCJSONConversions.h

Index: clangd/xpc/XPCJSONConversionTests.cpp
===================================================================
--- /dev/null
+++ clangd/xpc/XPCJSONConversionTests.cpp
@@ -0,0 +1,452 @@
+//===-- XPCJSONConversion.cpp  ------------------------*- C++ -*-----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/XPCJSONConversions.h"
+#include "gtest/gtest.h"
+
+#include <limits>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TEST(JsonXpcConversionTest, Null) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(nullptr)),
+      xpc_null_create()
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(nullptr) == xpcToJson(xpc_null_create())
+  );
+}
+
+TEST(JsonXpcConversionTest, Bool) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(true)),
+      xpc_bool_create(true)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(false) == xpcToJson(xpc_bool_create(false))
+  );
+}
+
+TEST(JsonXpcConversionTest, Number) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(3.14)),
+      xpc_double_create(3.14)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(3.14) == xpcToJson(xpc_double_create(3.14))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(42)),
+      xpc_double_create(42)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(42) == xpcToJson(xpc_double_create(42))
+  );
+  EXPECT_TRUE(
+    json::Expr(42) == xpcToJson(xpc_int64_create(42))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(-100)),
+      xpc_double_create(-100)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(-100) == xpcToJson(xpc_double_create(-100))
+  );
+
+  unsigned long long bigPositiveValue = std::numeric_limits<int>::max();
+  ++bigPositiveValue;
+  unsigned long long bigNegativeValue = std::numeric_limits<int>::min();
+  --bigNegativeValue;
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(bigPositiveValue)),
+      xpc_double_create(bigPositiveValue)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(bigPositiveValue) == xpcToJson(xpc_double_create(bigPositiveValue))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(bigNegativeValue)),
+      xpc_double_create(bigNegativeValue)
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(bigNegativeValue) == xpcToJson(xpc_double_create(bigNegativeValue))
+  );
+}
+
+TEST(JsonXpcConversionTest, String) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr("foo")),
+      xpc_string_create("foo")
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr("foo") == xpcToJson(xpc_string_create("foo"))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr("")),
+      xpc_string_create("")
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr("") == xpcToJson(xpc_string_create(""))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr("123")),
+      xpc_string_create("123")
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr("123") == xpcToJson(xpc_string_create("123"))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(" ")),
+      xpc_string_create(" ")
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(" ") == xpcToJson(xpc_string_create(" "))
+  );
+
+  // Testing two different "patterns" just in case.
+  std::string kBStringOfAs("A", 1024);
+  std::string kBStringOfBs("B", 1024);
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(kBStringOfAs.c_str())),
+      xpc_string_create(kBStringOfAs.c_str())
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(kBStringOfAs.c_str()) == xpcToJson(xpc_string_create(kBStringOfAs.c_str()))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Expr(kBStringOfBs.c_str())),
+      xpc_string_create(kBStringOfBs.c_str())
+    )
+  );
+  EXPECT_TRUE(
+    json::Expr(kBStringOfBs.c_str()) == xpcToJson(xpc_string_create(kBStringOfBs.c_str()))
+  );
+}
+
+TEST(JsonXpcConversionTest, Array) {
+  json::Expr JsonArray{true, "foo", nullptr, 42};
+
+  xpc_object_t XpcArray = []() {
+    std::vector<xpc_object_t> XpcArrayElements;
+    XpcArrayElements.emplace_back(xpc_bool_create(true));
+    XpcArrayElements.emplace_back(xpc_string_create("foo"));
+    XpcArrayElements.emplace_back(xpc_null_create());
+    XpcArrayElements.emplace_back(xpc_double_create(42));
+
+    return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+  }();
+
+  // Per-element checks just speed up eventual debugging - testing only one direction.
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray.asArray()->at(0)),
+      xpc_array_get_value(XpcArray, 0)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray.asArray()->at(1)),
+      xpc_array_get_value(XpcArray, 1)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray.asArray()->at(2)),
+      xpc_array_get_value(XpcArray, 2)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray.asArray()->at(3)),
+      xpc_array_get_value(XpcArray, 3)
+    )
+  );
+
+  json::Expr NestedJsonArray{ "foo", JsonArray, 123456789 };
+
+  xpc_object_t NestedXpcArray = [&](){
+    std::vector<xpc_object_t> NestedXpcArrayElements;
+    NestedXpcArrayElements.emplace_back(xpc_string_create("foo"));
+    NestedXpcArrayElements.emplace_back(XpcArray);
+    NestedXpcArrayElements.emplace_back(xpc_double_create(123456789));
+
+    return xpc_array_create(NestedXpcArrayElements.data(), NestedXpcArrayElements.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(NestedJsonArray),
+      NestedXpcArray
+    )
+  );
+  EXPECT_TRUE(
+    NestedJsonArray == xpcToJson(NestedXpcArray)
+  );
+}
+
+TEST(JsonXpcConversionTest, Dictionary) {
+  json::Expr JsonDict(
+    json::Expr::ObjectExpr{
+      {"a", true},
+      {"b", "foo"},
+      {"c", nullptr},
+      {"d", 42}
+    }
+  );
+
+  xpc_object_t XpcDict = []() {
+    std::vector<std::string> XpcDictKeys;
+    std::vector<xpc_object_t> XpcDictValues;
+    XpcDictKeys.emplace_back("a");
+    XpcDictValues.emplace_back(xpc_bool_create(true));
+    XpcDictKeys.emplace_back("b");
+    XpcDictValues.emplace_back(xpc_string_create("foo"));
+    XpcDictKeys.emplace_back("c");
+    XpcDictValues.emplace_back(xpc_null_create());
+    XpcDictKeys.emplace_back("d");
+    XpcDictValues.emplace_back(xpc_double_create(42));
+
+    std::vector<const char*> XpcDictKeysCStr = {
+      XpcDictKeys.at(0).c_str(),
+      XpcDictKeys.at(1).c_str(),
+      XpcDictKeys.at(2).c_str(),
+      XpcDictKeys.at(3).c_str()
+    };
+
+    return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+  }();
+
+  // Per-element checks just speed up eventual debugging - testing only one direction.
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.asObject()->get("a")),
+      xpc_dictionary_get_value(XpcDict, "a")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.asObject()->get("b")),
+      xpc_dictionary_get_value(XpcDict, "b")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.asObject()->get("c")),
+      xpc_dictionary_get_value(XpcDict, "c")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.asObject()->get("d")),
+      xpc_dictionary_get_value(XpcDict, "d")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonDict),
+      XpcDict
+    )
+  );
+
+  xpc_object_t NestedXpcDict = [&](){
+    std::vector<std::string> NestedXpcDictKeys;
+    std::vector<xpc_object_t> NestedXpcDictValues;
+    {
+      NestedXpcDictKeys.emplace_back("3");
+      NestedXpcDictValues.emplace_back(xpc_double_create(42.24));
+
+      NestedXpcDictKeys.emplace_back("3.1");
+      NestedXpcDictValues.emplace_back(XpcDict);
+
+      NestedXpcDictKeys.emplace_back("3.14");
+      NestedXpcDictValues.emplace_back(xpc_string_create("foo"));
+    }
+    std::vector<const char*> NestedXpcDictKeysCStr = {
+      NestedXpcDictKeys.at(0).c_str(),
+      NestedXpcDictKeys.at(1).c_str(),
+      NestedXpcDictKeys.at(2).c_str()
+    };
+    return xpc_dictionary_create(NestedXpcDictKeysCStr.data(), NestedXpcDictValues.data(), NestedXpcDictValues.size());
+  }();
+
+  json::Expr NestedJsonDict(
+    json::Expr::ObjectExpr{
+      {"3",     42.24},
+      {"3.1",   JsonDict},
+      {"3.14",  "foo"}
+    }
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(NestedJsonDict),
+      NestedXpcDict
+    )
+  );
+  EXPECT_TRUE(
+    NestedJsonDict == xpcToJson(NestedXpcDict)
+  );
+}
+
+TEST(JsonXpcConversionTest, ArrayContainingDictionary) {
+  json::Expr JsonDict(
+    json::Expr::ObjectExpr{
+      {"a", true},
+      {"b", "foo"},
+      {"c", nullptr},
+      {"d", 42}
+    }
+  );
+
+  json::Expr JsonArray{true, "foo", nullptr, JsonDict, 42};
+
+  xpc_object_t XpcArray = [](){
+    xpc_object_t XpcDict = [](){
+      std::vector<std::string> XpcDictKeys;
+      std::vector<xpc_object_t> XpcDictValues;
+      XpcDictKeys.emplace_back("a");
+      XpcDictValues.emplace_back(xpc_bool_create(true));
+      XpcDictKeys.emplace_back("b");
+      XpcDictValues.emplace_back(xpc_string_create("foo"));
+      XpcDictKeys.emplace_back("c");
+      XpcDictValues.emplace_back(xpc_null_create());
+      XpcDictKeys.emplace_back("d");
+      XpcDictValues.emplace_back(xpc_double_create(42));
+
+      std::vector<const char*> XpcDictKeysCStr = {
+        XpcDictKeys.at(0).c_str(),
+        XpcDictKeys.at(1).c_str(),
+        XpcDictKeys.at(2).c_str(),
+        XpcDictKeys.at(3).c_str()
+      };
+
+      return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+    }();
+
+    std::vector<xpc_object_t> XpcArrayElements;
+    XpcArrayElements.emplace_back(xpc_bool_create(true));
+    XpcArrayElements.emplace_back(xpc_string_create("foo"));
+    XpcArrayElements.emplace_back(xpc_null_create());
+    XpcArrayElements.emplace_back(XpcDict);
+    XpcArrayElements.emplace_back(xpc_double_create(42));
+
+    return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray),
+      XpcArray
+    )
+  );
+  EXPECT_TRUE(
+    JsonArray == xpcToJson(XpcArray)
+  );
+}
+
+TEST(JsonXpcConversionTest, DictionaryContainingArray) {
+  json::Expr JsonDict(
+    json::Expr::ObjectExpr{
+      {"a", true},
+      {"b", "foo"},
+      {"c", json::Expr{true, "foo", nullptr, 42}},
+      {"d", nullptr},
+      {"e", 42}
+    }
+  );
+
+  xpc_object_t XpcDict = [](){
+    xpc_object_t XpcArray = [](){
+      std::vector<xpc_object_t> XpcArrayElements;
+      XpcArrayElements.emplace_back(xpc_bool_create(true));
+      XpcArrayElements.emplace_back(xpc_string_create("foo"));
+      XpcArrayElements.emplace_back(xpc_null_create());
+      XpcArrayElements.emplace_back(xpc_double_create(42));
+
+      return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+    }();
+
+    std::vector<std::string> XpcDictKeys;
+    std::vector<xpc_object_t> XpcDictValues;
+    XpcDictKeys.emplace_back("a");
+    XpcDictValues.emplace_back(xpc_bool_create(true));
+    XpcDictKeys.emplace_back("b");
+    XpcDictValues.emplace_back(xpc_string_create("foo"));
+    XpcDictKeys.emplace_back("c");
+    XpcDictValues.emplace_back(XpcArray);
+    XpcDictKeys.emplace_back("d");
+    XpcDictValues.emplace_back(xpc_null_create());
+    XpcDictKeys.emplace_back("e");
+    XpcDictValues.emplace_back(xpc_double_create(42));
+
+    std::vector<const char*> XpcDictKeysCStr = {
+      XpcDictKeys.at(0).c_str(),
+      XpcDictKeys.at(1).c_str(),
+      XpcDictKeys.at(2).c_str(),
+      XpcDictKeys.at(3).c_str(),
+      XpcDictKeys.at(4).c_str()
+    };
+
+    return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonDict),
+      XpcDict
+    )
+  );
+  EXPECT_TRUE(
+    JsonDict == xpcToJson(XpcDict)
+  );
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clangd/xpc/CMakeLists.txt
===================================================================
--- /dev/null
+++ clangd/xpc/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANGD_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+  ${CLANGD_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangdXpcTests
+  XPCJSONConversionTests.cpp
+  )
+
+# FIXME: Factor out json::Expr from clangDaemon
+target_link_libraries(ClangdXpcTests
+  PRIVATE
+  clangdXpcJsonConversions
+  clangDaemon
+  LLVMSupport
+  LLVMTestingSupport
+  )
Index: xpc/XPCJSONConversions.h
===================================================================
--- /dev/null
+++ xpc/XPCJSONConversions.h
@@ -0,0 +1,25 @@
+//===--- XPCDispatcher.h - Main XPC service entry point ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCJSONCONVERSIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCJSONCONVERSIONS_H
+
+#include "JSONExpr.h"
+#include <xpc/xpc.h>
+
+namespace clang {
+namespace clangd {
+
+xpc_object_t jsonToXpc(const json::Expr& json);
+json::Expr xpcToJson(const xpc_object_t& json);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: xpc/XPCJSONConversions.cpp
===================================================================
--- /dev/null
+++ xpc/XPCJSONConversions.cpp
@@ -0,0 +1,88 @@
+//===--- XPCDispatcher.h - Main XPC service entry point ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/XPCJSONConversions.h"
+
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clangd;
+
+xpc_object_t clangd::jsonToXpc(const json::Expr& json) {
+  switch(json.kind()) {
+    case json::Expr::Null:    return xpc_null_create();
+    case json::Expr::Boolean: return xpc_bool_create(json.asBoolean().getValue());
+    case json::Expr::Number:  return xpc_double_create(json.asNumber().getValue());
+    case json::Expr::String:  return xpc_string_create(json.asString().getValue().str().c_str());
+    case json::Expr::Array:   {
+      std::vector<xpc_object_t> elements;
+      for(const auto& e : *json.asArray())
+        elements.emplace_back( jsonToXpc(e) );
+
+      return xpc_array_create(elements.data(), elements.size());
+    }
+    case json::Expr::Object: {
+      std::vector<std::string> keys;
+      std::vector<const char*> keysCStr;
+      std::vector<xpc_object_t> values;
+
+      for(const auto& k_v : *json.asObject()) {
+        keys.emplace_back(static_cast<llvm::StringRef>(k_v.first).str());
+        values.emplace_back(jsonToXpc(k_v.second));
+      }
+      // Get pointers only now so there's no possible re-allocation of keys.
+      for(const auto& k : keys)
+        keysCStr.emplace_back(k.c_str());
+
+      assert(keysCStr.size() == values.size() && "keys - data size mismatch");
+      return xpc_dictionary_create(keysCStr.data(), values.data(), keysCStr.size());
+    }
+  }
+  llvm_unreachable("unsupported JSON data type in jsonToXpc() conversion");
+}
+
+json::Expr clangd::xpcToJson(const xpc_object_t& xpcObj) {
+  const xpc_type_t objType = xpc_get_type(xpcObj);
+  if (objType == XPC_TYPE_NULL)
+    return json::Expr(nullptr);
+  if (objType == XPC_TYPE_BOOL)
+    return json::Expr(xpc_bool_get_value(xpcObj));
+  if (objType == XPC_TYPE_DOUBLE)
+    return json::Expr(xpc_double_get_value(xpcObj));
+  if (objType == XPC_TYPE_INT64)
+    return json::Expr(xpc_int64_get_value(xpcObj));
+  if (objType == XPC_TYPE_UINT64)
+    return json::Expr(xpc_uint64_get_value(xpcObj));
+  if (objType == XPC_TYPE_STRING)
+    return json::Expr(xpc_string_get_string_ptr(xpcObj));
+  if (objType == XPC_TYPE_ARRAY) {
+    __block json::Expr::ArrayExpr result;
+    xpc_array_apply(
+      xpcObj,
+      ^bool(size_t /* index */, xpc_object_t value) {
+        result.emplace_back(xpcToJson(value));
+        return true;
+      }
+    );
+    return std::move(result);
+  }
+  if (objType == XPC_TYPE_DICTIONARY) {
+    __block json::Expr::ObjectExpr result;
+    xpc_dictionary_apply(
+      xpcObj,
+      ^bool(const char *key, xpc_object_t value) {
+        result.emplace(json::Expr::ObjectKey(key), xpcToJson(value));
+        return true;
+      }
+    );
+    return std::move(result);
+  }
+  llvm_unreachable("unsupported JSON data type in xpcToJson() conversion");
+}
\ No newline at end of file
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to