Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clangd/xpc/XPCTransport.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | 0572611fdcc8 |
children | c4bab56944e8 |
line wrap: on
line source
//===--- XPCTransport.cpp - sending and receiving LSP messages over XPC ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Conversion.h" #include "Protocol.h" // For LSPError #include "Transport.h" #include "support/Logger.h" #include "llvm/Support/Errno.h" #include <xpc/xpc.h> using namespace llvm; using namespace clang; using namespace clangd; namespace { json::Object encodeError(Error E) { std::string Message; ErrorCode Code = ErrorCode::UnknownErrorCode; if (Error Unhandled = handleErrors(std::move(E), [&](const LSPError &L) -> Error { Message = L.Message; Code = L.Code; return Error::success(); })) Message = toString(std::move(Unhandled)); return json::Object{ {"message", std::move(Message)}, {"code", int64_t(Code)}, }; } Error decodeError(const json::Object &O) { std::string Msg = std::string(O.getString("message").getValueOr("Unspecified error")); if (auto Code = O.getInteger("code")) return make_error<LSPError>(std::move(Msg), ErrorCode(*Code)); return error("{0}", Msg); } // C "closure" for XPCTransport::loop() method namespace xpcClosure { void connection_handler(xpc_connection_t clientConnection); } class XPCTransport : public Transport { public: XPCTransport() {} void notify(StringRef Method, json::Value Params) override { sendMessage(json::Object{ {"jsonrpc", "2.0"}, {"method", Method}, {"params", std::move(Params)}, }); } void call(StringRef Method, json::Value Params, json::Value ID) override { sendMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", std::move(ID)}, {"method", Method}, {"params", std::move(Params)}, }); } void reply(json::Value ID, Expected<json::Value> Result) override { if (Result) { sendMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", std::move(ID)}, {"result", std::move(*Result)}, }); } else { sendMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", std::move(ID)}, {"error", encodeError(Result.takeError())}, }); } } Error loop(MessageHandler &Handler) override; private: // Needs access to handleMessage() and resetClientConnection() friend void xpcClosure::connection_handler(xpc_connection_t clientConnection); // Dispatches incoming message to Handler onNotify/onCall/onReply. bool handleMessage(json::Value Message, MessageHandler &Handler); void sendMessage(json::Value Message) { xpc_object_t response = jsonToXpc(Message); xpc_connection_send_message(clientConnection, response); xpc_release(response); } void resetClientConnection(xpc_connection_t newClientConnection) { clientConnection = newClientConnection; } xpc_connection_t clientConnection; }; bool XPCTransport::handleMessage(json::Value Message, MessageHandler &Handler) { // Message must be an object with "jsonrpc":"2.0". auto *Object = Message.getAsObject(); if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0")) { elog("Not a JSON-RPC 2.0 message: {0:2}", Message); return false; } // ID may be any JSON value. If absent, this is a notification. Optional<json::Value> ID; if (auto *I = Object->get("id")) ID = std::move(*I); auto Method = Object->getString("method"); if (!Method) { // This is a response. if (!ID) { elog("No method and no response ID: {0:2}", Message); return false; } if (auto *Err = Object->getObject("error")) return Handler.onReply(std::move(*ID), decodeError(*Err)); // Result should be given, use null if not. json::Value Result = nullptr; if (auto *R = Object->get("result")) Result = std::move(*R); return Handler.onReply(std::move(*ID), std::move(Result)); } // Params should be given, use null if not. json::Value Params = nullptr; if (auto *P = Object->get("params")) Params = std::move(*P); if (ID) return Handler.onCall(*Method, std::move(Params), std::move(*ID)); else return Handler.onNotify(*Method, std::move(Params)); } namespace xpcClosure { // "owner" of this "closure object" - necessary for propagating connection to // XPCTransport so it can send messages to the client. XPCTransport *TransportObject = nullptr; Transport::MessageHandler *HandlerPtr = nullptr; void connection_handler(xpc_connection_t clientConnection) { xpc_connection_set_target_queue(clientConnection, dispatch_get_main_queue()); xpc_transaction_begin(); TransportObject->resetClientConnection(clientConnection); xpc_connection_set_event_handler(clientConnection, ^(xpc_object_t message) { if (message == XPC_ERROR_CONNECTION_INVALID) { // connection is being terminated log("Received XPC_ERROR_CONNECTION_INVALID message - returning from the " "event_handler."); return; } if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) { log("Received XPC message of unknown type - returning from the " "event_handler."); return; } const json::Value Doc = xpcToJson(message); if (Doc == json::Value(nullptr)) { log("XPC message was converted to Null JSON message - returning from the " "event_handler."); return; } vlog("<<< {0}\n", Doc); if (!TransportObject->handleMessage(std::move(Doc), *HandlerPtr)) { log("Received exit notification - cancelling connection."); xpc_connection_cancel(xpc_dictionary_get_remote_connection(message)); xpc_transaction_end(); } }); xpc_connection_resume(clientConnection); } } // namespace xpcClosure Error XPCTransport::loop(MessageHandler &Handler) { assert(xpcClosure::TransportObject == nullptr && "TransportObject has already been set."); // This looks scary since lifetime of this (or any) XPCTransport object has // to fully contain lifetime of any XPC connection. In practise any Transport // object is destroyed only at the end of main() which is always after // exit of xpc_main(). xpcClosure::TransportObject = this; assert(xpcClosure::HandlerPtr == nullptr && "HandlerPtr has already been set."); xpcClosure::HandlerPtr = &Handler; xpc_main(xpcClosure::connection_handler); // xpc_main doesn't ever return return errorCodeToError(std::make_error_code(std::errc::io_error)); } } // namespace namespace clang { namespace clangd { std::unique_ptr<Transport> newXPCTransport() { return std::make_unique<XPCTransport>(); } } // namespace clangd } // namespace clang