clang-tools 10.0.0

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