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: 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 + +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::max(); + ++bigPositiveValue; + unsigned long long bigNegativeValue = std::numeric_limits::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 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 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 XpcDictKeys; + std::vector 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 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 NestedXpcDictKeys; + std::vector 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 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 XpcDictKeys; + std::vector 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 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 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 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 XpcDictKeys; + std::vector 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 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: 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 + +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 +#include + +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 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 keys; + std::vector keysCStr; + std::vector values; + + for(const auto& k_v : *json.asObject()) { + keys.emplace_back(static_cast(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