clang-tools  14.0.0git
XPCTransport.cpp
Go to the documentation of this file.
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "Conversion.h"
9 #include "Protocol.h" // For LSPError
10 #include "Transport.h"
11 #include "support/Logger.h"
12 #include "llvm/Support/Errno.h"
13 
14 #include <xpc/xpc.h>
15 
16 using namespace llvm;
17 using namespace clang;
18 using namespace clangd;
19 
20 namespace {
21 
22 json::Object encodeError(Error E) {
23  std::string Message;
24  ErrorCode Code = ErrorCode::UnknownErrorCode;
25  if (Error Unhandled =
26  handleErrors(std::move(E), [&](const LSPError &L) -> Error {
27  Message = L.Message;
28  Code = L.Code;
29  return Error::success();
30  }))
31  Message = toString(std::move(Unhandled));
32 
33  return json::Object{
34  {"message", std::move(Message)},
35  {"code", int64_t(Code)},
36  };
37 }
38 
39 Error decodeError(const json::Object &O) {
40  std::string Msg =
41  std::string(O.getString("message").getValueOr("Unspecified error"));
42  if (auto Code = O.getInteger("code"))
43  return make_error<LSPError>(std::move(Msg), ErrorCode(*Code));
44  return error("{0}", Msg);
45 }
46 
47 // C "closure" for XPCTransport::loop() method
48 namespace xpcClosure {
49 void connection_handler(xpc_connection_t clientConnection);
50 } // namespace xpcClosure
51 
52 class XPCTransport : public Transport {
53 public:
54  XPCTransport() {}
55 
56  void notify(StringRef Method, json::Value Params) override {
57  sendMessage(json::Object{
58  {"jsonrpc", "2.0"},
59  {"method", Method},
60  {"params", std::move(Params)},
61  });
62  }
63  void call(StringRef Method, json::Value Params, json::Value ID) override {
64  sendMessage(json::Object{
65  {"jsonrpc", "2.0"},
66  {"id", std::move(ID)},
67  {"method", Method},
68  {"params", std::move(Params)},
69  });
70  }
71  void reply(json::Value ID, Expected<json::Value> Result) override {
72  if (Result) {
73  sendMessage(json::Object{
74  {"jsonrpc", "2.0"},
75  {"id", std::move(ID)},
76  {"result", std::move(*Result)},
77  });
78  } else {
79  sendMessage(json::Object{
80  {"jsonrpc", "2.0"},
81  {"id", std::move(ID)},
82  {"error", encodeError(Result.takeError())},
83  });
84  }
85  }
86 
87  Error loop(MessageHandler &Handler) override;
88 
89 private:
90  // Needs access to handleMessage() and resetClientConnection()
91  friend void xpcClosure::connection_handler(xpc_connection_t clientConnection);
92 
93  // Dispatches incoming message to Handler onNotify/onCall/onReply.
94  bool handleMessage(json::Value Message, MessageHandler &Handler);
95  void sendMessage(json::Value Message) {
96  xpc_object_t response = jsonToXpc(Message);
97  xpc_connection_send_message(clientConnection, response);
98  xpc_release(response);
99  }
100  void resetClientConnection(xpc_connection_t newClientConnection) {
101  clientConnection = newClientConnection;
102  }
103  xpc_connection_t clientConnection;
104 };
105 
106 bool XPCTransport::handleMessage(json::Value Message, MessageHandler &Handler) {
107  // Message must be an object with "jsonrpc":"2.0".
108  auto *Object = Message.getAsObject();
109  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0")) {
110  elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
111  return false;
112  }
113  // ID may be any JSON value. If absent, this is a notification.
114  Optional<json::Value> ID;
115  if (auto *I = Object->get("id"))
116  ID = std::move(*I);
117  auto Method = Object->getString("method");
118  if (!Method) { // This is a response.
119  if (!ID) {
120  elog("No method and no response ID: {0:2}", Message);
121  return false;
122  }
123  if (auto *Err = Object->getObject("error"))
124  return Handler.onReply(std::move(*ID), decodeError(*Err));
125  // Result should be given, use null if not.
126  json::Value Result = nullptr;
127  if (auto *R = Object->get("result"))
128  Result = std::move(*R);
129  return Handler.onReply(std::move(*ID), std::move(Result));
130  }
131  // Params should be given, use null if not.
132  json::Value Params = nullptr;
133  if (auto *P = Object->get("params"))
134  Params = std::move(*P);
135 
136  if (ID)
137  return Handler.onCall(*Method, std::move(Params), std::move(*ID));
138  else
139  return Handler.onNotify(*Method, std::move(Params));
140 }
141 
142 namespace xpcClosure {
143 // "owner" of this "closure object" - necessary for propagating connection to
144 // XPCTransport so it can send messages to the client.
145 XPCTransport *TransportObject = nullptr;
146 Transport::MessageHandler *HandlerPtr = nullptr;
147 
148 void connection_handler(xpc_connection_t clientConnection) {
149  xpc_connection_set_target_queue(clientConnection, dispatch_get_main_queue());
150 
151  xpc_transaction_begin();
152 
153  TransportObject->resetClientConnection(clientConnection);
154 
155  xpc_connection_set_event_handler(clientConnection, ^(xpc_object_t message) {
156  if (message == XPC_ERROR_CONNECTION_INVALID) {
157  // connection is being terminated
158  log("Received XPC_ERROR_CONNECTION_INVALID message - returning from the "
159  "event_handler.");
160  return;
161  }
162 
163  if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {
164  log("Received XPC message of unknown type - returning from the "
165  "event_handler.");
166  return;
167  }
168 
169  const json::Value Doc = xpcToJson(message);
170  if (Doc == json::Value(nullptr)) {
171  log("XPC message was converted to Null JSON message - returning from the "
172  "event_handler.");
173  return;
174  }
175 
176  vlog("<<< {0}\n", Doc);
177 
178  if (!TransportObject->handleMessage(std::move(Doc), *HandlerPtr)) {
179  log("Received exit notification - cancelling connection.");
180  xpc_connection_cancel(xpc_dictionary_get_remote_connection(message));
181  xpc_transaction_end();
182  }
183  });
184 
185  xpc_connection_resume(clientConnection);
186 }
187 } // namespace xpcClosure
188 
189 Error XPCTransport::loop(MessageHandler &Handler) {
190  assert(xpcClosure::TransportObject == nullptr &&
191  "TransportObject has already been set.");
192  // This looks scary since lifetime of this (or any) XPCTransport object has
193  // to fully contain lifetime of any XPC connection. In practise any Transport
194  // object is destroyed only at the end of main() which is always after
195  // exit of xpc_main().
196  xpcClosure::TransportObject = this;
197 
198  assert(xpcClosure::HandlerPtr == nullptr &&
199  "HandlerPtr has already been set.");
200  xpcClosure::HandlerPtr = &Handler;
201 
202  xpc_main(xpcClosure::connection_handler);
203  // xpc_main doesn't ever return
204  return errorCodeToError(std::make_error_code(std::errc::io_error));
205 }
206 
207 } // namespace
208 
209 namespace clang {
210 namespace clangd {
211 
212 std::unique_ptr<Transport> newXPCTransport() {
213  return std::make_unique<XPCTransport>();
214 }
215 
216 } // namespace clangd
217 } // namespace clang
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::clangd::SymbolKind::Object
@ Object
clang::clangd::detail::error
llvm::Error error(std::error_code, std::string &&)
Definition: Logger.cpp:80
clang::tidy::bugprone::Message
static const char Message[]
Definition: ReservedIdentifierCheck.cpp:31
clang::clangd::xpcToJson
json::Value xpcToJson(const xpc_object_t &XPCObject)
Definition: Conversion.cpp:26
xpcClosure
Definition: XPCTransport.cpp:48
clang::tidy::handleErrors
void handleErrors(llvm::ArrayRef< ClangTidyError > Errors, ClangTidyContext &Context, FixBehaviour Fix, unsigned &WarningsAsErrorsCount, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS)
Displays the found Errors to the users.
Definition: ClangTidy.cpp:591
clang::clangd::ErrorCode
ErrorCode
Definition: Protocol.h:47
Protocol.h
clang::clangd::newXPCTransport
std::unique_ptr< Transport > newXPCTransport()
Definition: XPCTransport.cpp:212
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::LSPError
Definition: Protocol.h:63
Logger.h
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:73
clang::clangd::CompletionItemKind::Method
@ Method
clang::clangd::Transport::MessageHandler
Definition: Transport.h:47
clang::clangd::Transport
Definition: Transport.h:35
Transport.h
ID
static char ID
Definition: Logger.cpp:74
Conversion.h
clang::clangd::jsonToXpc
xpc_object_t jsonToXpc(const json::Value &JSON)
Definition: Conversion.cpp:20
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:55
clang::clangd::elog
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
clang::clangd::detail::log
void log(Logger::Level L, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:48
clang::clangd::MessageType::Error
@ Error
An error message.
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72