clang-tools 8.0.0

XPCTransport.cpp
Go to the documentation of this file.
1 //===--- XPCTransport.cpp - sending and receiving LSP messages over XPC ---===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "Conversion.h"
10 #include "Logger.h"
11 #include "Protocol.h" // For LSPError
12 #include "Transport.h"
13 #include "llvm/Support/Errno.h"
14
15 #include <xpc/xpc.h>
16
17 using namespace llvm;
18 using namespace clang;
19 using namespace clangd;
20
21 namespace {
22
23 json::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
40 Error decodeError(const json::Object &O) {
41 std::string Msg = 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 make_error<StringError>(std::move(Msg), inconvertibleErrorCode());
45 }
46
47 // C "closure" for XPCTransport::loop() method
48 namespace xpcClosure {
49 void connection_handler(xpc_connection_t clientConnection);
50 }
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 llvm::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
clang::clangd::detail::log
void log(Logger::Level, const llvm::formatv_object_base &)
Definition: Logger.cpp:31
clang::clangd::Transport
Definition: Transport.h:35
clang::clangd::newXPCTransport
std::unique_ptr< Transport > newXPCTransport()
Definition: XPCTransport.cpp:212
Protocol.h
clang::clangd::LSPError
Definition: Protocol.h:53
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:59
clang::tidy::modernize::Message
static const StringRef Message
Definition: UseTransparentFunctorsCheck.cpp:71
Conversion.h
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:68
clang::clangd::elog
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:57
xpcClosure
Definition: XPCTransport.cpp:48
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:581
Transport.h
clang::clangd::SymbolKind::Object
clang::clangd::Transport::MessageHandler
Definition: Transport.h:47
clang::clangd::xpcToJson
json::Value xpcToJson(const xpc_object_t &XPCObject)
Definition: Conversion.cpp:27
clang::clangd::ErrorCode
ErrorCode
Definition: Protocol.h:38
clang::clangd::LSPError::Message
std::string Message
Definition: Protocol.h:55
clang::clangd::LSPError::Code
ErrorCode Code
Definition: Protocol.h:56
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang::clangd::CompletionItemKind::Method
Logger.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::clangd::jsonToXpc
xpc_object_t jsonToXpc(const json::Value &JSON)
Definition: Conversion.cpp:21