brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.7 KiB · 8f5b787 Raw
220 lines · cpp
1//===--- XPCTransport.cpp - sending and receiving LSP messages over XPC ---===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "Conversion.h"9#include "Protocol.h" // For LSPError10#include "Transport.h"11#include "support/Logger.h"12#include "llvm/Support/Errno.h"13 14#include <optional>15#include <xpc/xpc.h>16 17using namespace llvm;18using namespace clang;19using namespace clangd;20 21namespace {22 23json::Object encodeError(Error E) {24  std::string Message;25  ErrorCode Code = ErrorCode::UnknownErrorCode;26  if (Error Unhandled =27          handleErrors(std::move(E), [&](const LSPError &L) -> Error {28            Message = L.Message;29            Code = L.Code;30            return Error::success();31          }))32    Message = toString(std::move(Unhandled));33 34  return json::Object{35      {"message", std::move(Message)},36      {"code", int64_t(Code)},37  };38}39 40Error decodeError(const json::Object &O) {41  std::string Msg =42      std::string(O.getString("message").value_or("Unspecified error"));43  if (auto Code = O.getInteger("code"))44    return make_error<LSPError>(std::move(Msg), ErrorCode(*Code));45  return error("{0}", Msg);46}47 48// C "closure" for XPCTransport::loop() method49namespace xpcClosure {50void connection_handler(xpc_connection_t clientConnection);51} // namespace xpcClosure52 53class XPCTransport : public Transport {54public:55  XPCTransport() {}56 57  void notify(StringRef Method, json::Value Params) override {58    sendMessage(json::Object{59        {"jsonrpc", "2.0"},60        {"method", Method},61        {"params", std::move(Params)},62    });63  }64  void call(StringRef Method, json::Value Params, json::Value ID) override {65    sendMessage(json::Object{66        {"jsonrpc", "2.0"},67        {"id", std::move(ID)},68        {"method", Method},69        {"params", std::move(Params)},70    });71  }72  void reply(json::Value ID, Expected<json::Value> Result) override {73    if (Result) {74      sendMessage(json::Object{75          {"jsonrpc", "2.0"},76          {"id", std::move(ID)},77          {"result", std::move(*Result)},78      });79    } else {80      sendMessage(json::Object{81          {"jsonrpc", "2.0"},82          {"id", std::move(ID)},83          {"error", encodeError(Result.takeError())},84      });85    }86  }87 88  Error loop(MessageHandler &Handler) override;89 90private:91  // Needs access to handleMessage() and resetClientConnection()92  friend void xpcClosure::connection_handler(xpc_connection_t clientConnection);93 94  // Dispatches incoming message to Handler onNotify/onCall/onReply.95  bool handleMessage(json::Value Message, MessageHandler &Handler);96  void sendMessage(json::Value Message) {97    xpc_object_t response = jsonToXpc(Message);98    xpc_connection_send_message(clientConnection, response);99    xpc_release(response);100  }101  void resetClientConnection(xpc_connection_t newClientConnection) {102    clientConnection = newClientConnection;103  }104  xpc_connection_t clientConnection;105};106 107bool XPCTransport::handleMessage(json::Value Message, MessageHandler &Handler) {108  // Message must be an object with "jsonrpc":"2.0".109  auto *Object = Message.getAsObject();110  if (!Object ||111      Object->getString("jsonrpc") != std::optional<StringRef>("2.0")) {112    elog("Not a JSON-RPC 2.0 message: {0:2}", Message);113    return false;114  }115  // ID may be any JSON value. If absent, this is a notification.116  std::optional<json::Value> ID;117  if (auto *I = Object->get("id"))118    ID = std::move(*I);119  auto Method = Object->getString("method");120  if (!Method) { // This is a response.121    if (!ID) {122      elog("No method and no response ID: {0:2}", Message);123      return false;124    }125    if (auto *Err = Object->getObject("error"))126      return Handler.onReply(std::move(*ID), decodeError(*Err));127    // Result should be given, use null if not.128    json::Value Result = nullptr;129    if (auto *R = Object->get("result"))130      Result = std::move(*R);131    return Handler.onReply(std::move(*ID), std::move(Result));132  }133  // Params should be given, use null if not.134  json::Value Params = nullptr;135  if (auto *P = Object->get("params"))136    Params = std::move(*P);137 138  if (ID)139    return Handler.onCall(*Method, std::move(Params), std::move(*ID));140  else141    return Handler.onNotify(*Method, std::move(Params));142}143 144namespace xpcClosure {145// "owner" of this "closure object" - necessary for propagating connection to146// XPCTransport so it can send messages to the client.147XPCTransport *TransportObject = nullptr;148Transport::MessageHandler *HandlerPtr = nullptr;149 150void connection_handler(xpc_connection_t clientConnection) {151  xpc_connection_set_target_queue(clientConnection, dispatch_get_main_queue());152 153  xpc_transaction_begin();154 155  TransportObject->resetClientConnection(clientConnection);156 157  xpc_connection_set_event_handler(clientConnection, ^(xpc_object_t message) {158    if (message == XPC_ERROR_CONNECTION_INVALID) {159      // connection is being terminated160      log("Received XPC_ERROR_CONNECTION_INVALID message - returning from the "161          "event_handler.");162      return;163    }164 165    if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {166      log("Received XPC message of unknown type - returning from the "167          "event_handler.");168      return;169    }170 171    const json::Value Doc = xpcToJson(message);172    if (Doc == json::Value(nullptr)) {173      log("XPC message was converted to Null JSON message - returning from the "174          "event_handler.");175      return;176    }177 178    vlog("<<< {0}\n", Doc);179 180    if (!TransportObject->handleMessage(std::move(Doc), *HandlerPtr)) {181      log("Received exit notification - cancelling connection.");182      xpc_connection_cancel(xpc_dictionary_get_remote_connection(message));183      xpc_transaction_end();184    }185  });186 187  xpc_connection_resume(clientConnection);188}189} // namespace xpcClosure190 191Error XPCTransport::loop(MessageHandler &Handler) {192  assert(xpcClosure::TransportObject == nullptr &&193         "TransportObject has already been set.");194  // This looks scary since lifetime of this (or any) XPCTransport object has195  // to fully contain lifetime of any XPC connection. In practise any Transport196  // object is destroyed only at the end of main() which is always after197  // exit of xpc_main().198  xpcClosure::TransportObject = this;199 200  assert(xpcClosure::HandlerPtr == nullptr &&201         "HandlerPtr has already been set.");202  xpcClosure::HandlerPtr = &Handler;203 204  xpc_main(xpcClosure::connection_handler);205  // xpc_main doesn't ever return206  return errorCodeToError(std::make_error_code(std::errc::io_error));207}208 209} // namespace210 211namespace clang {212namespace clangd {213 214std::unique_ptr<Transport> newXPCTransport() {215  return std::make_unique<XPCTransport>();216}217 218} // namespace clangd219} // namespace clang220