LLVM 22.0.0git
Transport.cpp
Go to the documentation of this file.
1//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===//
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
11#include "llvm/Support/Error.h"
14#include <atomic>
15#include <optional>
16#include <system_error>
17#include <utility>
18
19using namespace llvm;
20using namespace llvm::lsp;
21
22//===----------------------------------------------------------------------===//
23// Reply
24//===----------------------------------------------------------------------===//
25
26namespace {
27/// Function object to reply to an LSP call.
28/// Each instance must be called exactly once, otherwise:
29/// - if there was no reply, an error reply is sent
30/// - if there were multiple replies, only the first is sent
31class Reply {
32public:
33 Reply(const llvm::json::Value &Id, StringRef Method, JSONTransport &Transport,
34 std::mutex &TransportOutputMutex);
35 Reply(Reply &&Other);
36 Reply &operator=(Reply &&) = delete;
37 Reply(const Reply &) = delete;
38 Reply &operator=(const Reply &) = delete;
39
40 void operator()(llvm::Expected<llvm::json::Value> Reply);
41
42private:
43 std::string Method;
44 std::atomic<bool> Replied = {false};
45 llvm::json::Value Id;
46 JSONTransport *Transport;
47 std::mutex &TransportOutputMutex;
48};
49} // namespace
50
51Reply::Reply(const llvm::json::Value &Id, llvm::StringRef Method,
52 JSONTransport &Transport, std::mutex &TransportOutputMutex)
53 : Method(Method), Id(Id), Transport(&Transport),
54 TransportOutputMutex(TransportOutputMutex) {}
55
56Reply::Reply(Reply &&Other)
57 : Method(Other.Method), Replied(Other.Replied.load()),
58 Id(std::move(Other.Id)), Transport(Other.Transport),
59 TransportOutputMutex(Other.TransportOutputMutex) {
60 Other.Transport = nullptr;
61}
62
63void Reply::operator()(llvm::Expected<llvm::json::Value> Reply) {
64 if (Replied.exchange(true)) {
65 Logger::error("Replied twice to message {0}({1})", Method, Id);
66 assert(false && "must reply to each call only once!");
67 return;
68 }
69 assert(Transport && "expected valid transport to reply to");
70
71 std::lock_guard<std::mutex> TransportLock(TransportOutputMutex);
72 if (Reply) {
73 Logger::info("--> reply:{0}({1})", Method, Id);
74 Transport->reply(std::move(Id), std::move(Reply));
75 } else {
76 llvm::Error Error = Reply.takeError();
77 Logger::info("--> reply:{0}({1}): {2}", Method, Id, Error);
78 Transport->reply(std::move(Id), std::move(Error));
79 }
80}
81
82//===----------------------------------------------------------------------===//
83// MessageHandler
84//===----------------------------------------------------------------------===//
85
87 Logger::info("--> {0}", Method);
88
89 if (Method == "exit")
90 return false;
91 if (Method == "$cancel") {
92 // TODO: Add support for cancelling requests.
93 } else {
94 auto It = NotificationHandlers.find(Method);
95 if (It != NotificationHandlers.end())
96 It->second(std::move(Value));
97 }
98 return true;
99}
100
103 Logger::info("--> {0}({1})", Method, Id);
104
105 Reply Reply(Id, Method, Transport, TransportOutputMutex);
106
107 auto It = MethodHandlers.find(Method);
108 if (It != MethodHandlers.end()) {
109 It->second(std::move(Params), std::move(Reply));
110 } else {
111 Reply(llvm::make_error<LSPError>("method not found: " + Method.str(),
113 }
114 return true;
115}
116
119 // Find the response handler in the mapping. If it exists, move it out of the
120 // mapping and erase it.
121 ResponseHandlerTy ResponseHandler;
122 {
123 std::lock_guard<std::mutex> responseHandlersLock(ResponseHandlerTy);
124 auto It = ResponseHandlers.find(debugString(Id));
125 if (It != ResponseHandlers.end()) {
126 ResponseHandler = std::move(It->second);
127 ResponseHandlers.erase(It);
128 }
129 }
130
131 // If we found a response handler, invoke it. Otherwise, log an error.
132 if (ResponseHandler.second) {
133 Logger::info("--> reply:{0}({1})", ResponseHandler.first, Id);
134 ResponseHandler.second(std::move(Id), std::move(Result));
135 } else {
137 "received a reply with ID {0}, but there was no such outgoing request",
138 Id);
139 if (!Result)
140 llvm::consumeError(Result.takeError());
141 }
142 return true;
143}
144
145//===----------------------------------------------------------------------===//
146// JSONTransport
147//===----------------------------------------------------------------------===//
148
149/// Encode the given error as a JSON object.
151 std::string Message;
153 auto HandlerFn = [&](const LSPError &LspError) -> llvm::Error {
154 Message = LspError.message;
155 Code = LspError.code;
156 return llvm::Error::success();
157 };
158 if (llvm::Error Unhandled = llvm::handleErrors(std::move(Error), HandlerFn))
159 Message = llvm::toString(std::move(Unhandled));
160
161 return llvm::json::Object{
162 {"message", std::move(Message)},
163 {"code", int64_t(Code)},
164 };
165}
166
167/// Decode the given JSON object into an error.
169 StringRef Msg = O.getString("message").value_or("Unspecified error");
170 if (std::optional<int64_t> Code = O.getInteger("code"))
171 return llvm::make_error<LSPError>(Msg.str(), ErrorCode(*Code));
173 Msg.str());
174}
175
177 sendMessage(llvm::json::Object{
178 {"jsonrpc", "2.0"},
179 {"method", Method},
180 {"params", std::move(Params)},
181 });
182}
185 sendMessage(llvm::json::Object{
186 {"jsonrpc", "2.0"},
187 {"id", std::move(Id)},
188 {"method", Method},
189 {"params", std::move(Params)},
190 });
191}
194 if (Result) {
195 return sendMessage(llvm::json::Object{
196 {"jsonrpc", "2.0"},
197 {"id", std::move(Id)},
198 {"result", std::move(*Result)},
199 });
200 }
201
202 sendMessage(llvm::json::Object{
203 {"jsonrpc", "2.0"},
204 {"id", std::move(Id)},
205 {"error", encodeError(Result.takeError())},
206 });
207}
208
210 std::string Json;
211 while (!In->isEndOfInput()) {
212 if (In->hasError()) {
214 std::error_code(errno, std::system_category()));
215 }
216
217 if (succeeded(In->readMessage(Json))) {
219 if (!handleMessage(std::move(*Doc), Handler))
220 return llvm::Error::success();
221 } else {
222 Logger::error("JSON parse error: {0}", llvm::toString(Doc.takeError()));
223 }
224 }
225 }
226 return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
227}
228
229void JSONTransport::sendMessage(llvm::json::Value Msg) {
230 OutputBuffer.clear();
232 os << llvm::formatv(PrettyOutput ? "{0:2}\n" : "{0}", Msg);
233 Out << "Content-Length: " << OutputBuffer.size() << "\r\n\r\n"
234 << OutputBuffer;
235 Out.flush();
236 Logger::debug(">>> {0}\n", OutputBuffer);
237}
238
239bool JSONTransport::handleMessage(llvm::json::Value Msg,
240 MessageHandler &Handler) {
241 // Message must be an object with "jsonrpc":"2.0".
243 if (!Object ||
244 Object->getString("jsonrpc") != std::optional<StringRef>("2.0"))
245 return false;
246
247 // `id` may be any JSON value. If absent, this is a notification.
248 std::optional<llvm::json::Value> Id;
249 if (llvm::json::Value *I = Object->get("id"))
250 Id = std::move(*I);
251 std::optional<StringRef> Method = Object->getString("method");
252
253 // This is a response.
254 if (!Method) {
255 if (!Id)
256 return false;
257 if (auto *Err = Object->getObject("error"))
258 return Handler.onReply(std::move(*Id), decodeError(*Err));
259 // result should be given, use null if not.
260 llvm::json::Value Result = nullptr;
261 if (llvm::json::Value *R = Object->get("result"))
262 Result = std::move(*R);
263 return Handler.onReply(std::move(*Id), std::move(Result));
264 }
265
266 // Params should be given, use null if not.
267 llvm::json::Value Params = nullptr;
268 if (llvm::json::Value *P = Object->get("params"))
269 Params = std::move(*P);
270
271 if (Id)
272 return Handler.onCall(*Method, std::move(Params), std::move(*Id));
273 return Handler.onNotify(*Method, std::move(Params));
274}
275
276/// Tries to read a line up to and including \n.
277/// If failing, feof(), ferror(), or shutdownRequested() will be set.
279 // Big enough to hold any reasonable header line. May not fit content lines
280 // in delimited mode, but performance doesn't matter for that mode.
281 static constexpr int BufSize = 128;
282 size_t Size = 0;
283 Out.clear();
284 for (;;) {
285 Out.resize_for_overwrite(Size + BufSize);
286 if (!std::fgets(&Out[Size], BufSize, In))
287 return failure();
288
289 clearerr(In);
290
291 // If the line contained null bytes, anything after it (including \n) will
292 // be ignored. Fortunately this is not a legal header or JSON.
293 size_t Read = std::strlen(&Out[Size]);
294 if (Read > 0 && Out[Size + Read - 1] == '\n') {
295 Out.resize(Size + Read);
296 return success();
297 }
298 Size += Read;
299 }
300}
301
302// Returns std::nullopt when:
303// - ferror(), feof(), or shutdownRequested() are set.
304// - Content-Length is missing or empty (protocol error)
305LogicalResult
307 // A Language Server Protocol message starts with a set of HTTP headers,
308 // delimited by \r\n, and terminated by an empty line (\r\n).
309 unsigned long long ContentLength = 0;
311 while (true) {
312 if (feof(In) || hasError() || failed(readLine(In, Line)))
313 return failure();
314
315 // Content-Length is a mandatory header, and the only one we handle.
316 StringRef LineRef = Line;
317 if (LineRef.consume_front("Content-Length: ")) {
318 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
319 } else if (!LineRef.trim().empty()) {
320 // It's another header, ignore it.
321 continue;
322 } else {
323 // An empty line indicates the end of headers. Go ahead and read the JSON.
324 break;
325 }
326 }
327
328 // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
329 if (ContentLength == 0 || ContentLength > 1 << 30)
330 return failure();
331
332 Json.resize(ContentLength);
333 for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
334 Read = std::fread(&Json[Pos], 1, ContentLength - Pos, In);
335 if (Read == 0)
336 return failure();
337
338 // If we're done, the error was transient. If we're not done, either it was
339 // transient or we'll see it again on retry.
340 clearerr(In);
341 Pos += Read;
342 }
343 return success();
344}
345
346/// For lit tests we support a simplified syntax:
347/// - messages are delimited by '// -----' on a line by itself
348/// - lines starting with // are ignored.
349/// This is a testing path, so favor simplicity over performance here.
350/// When returning failure: feof(), ferror(), or shutdownRequested() will be
351/// set.
354 Json.clear();
356 while (succeeded(readLine(In, Line))) {
357 StringRef LineRef = Line.str().trim();
358 if (LineRef.starts_with("//")) {
359 // Found a delimiter for the message.
360 if (LineRef == "// -----")
361 break;
362 continue;
363 }
364
365 Json += Line;
366 }
367
368 return failure(ferror(In));
369}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Mark last scratch load
#define I(x, y, z)
Definition MD5.cpp:58
#define P(N)
This file defines the SmallString class.
static llvm::json::Object encodeError(llvm::Error Error)
Encode the given error as a JSON object.
LogicalResult readLine(std::FILE *In, SmallVectorImpl< char > &Out)
Tries to read a line up to and including .
llvm::Error decodeError(const llvm::json::Object &O)
Decode the given JSON object into an error.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
void resize(size_type N)
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
std::string str() const
str - Get the contents as an std::string.
Definition StringRef.h:233
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:269
constexpr bool empty() const
empty - Check if the string is empty.
Definition StringRef.h:151
bool consume_front(StringRef Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition StringRef.h:645
StringRef trim(char Char) const
Return string with consecutive Char characters starting from the left and right removed.
Definition StringRef.h:824
An Object is a JSON object, which maps strings to heterogenous JSON values.
Definition JSON.h:98
A Value is an JSON value of unknown type.
Definition JSON.h:290
const json::Object * getAsObject() const
Definition JSON.h:464
LogicalResult readStandardMessage(std::string &Json) final
LogicalResult readDelimitedMessage(std::string &Json) final
For lit tests we support a simplified syntax:
A transport class that performs the JSON-RPC communication with the LSP client.
Definition Transport.h:93
void reply(llvm::json::Value Id, llvm::Expected< llvm::json::Value > Result)
llvm::Error run(MessageHandler &Handler)
Start executing the JSON-RPC transport.
void notify(StringRef Method, llvm::json::Value Params)
The following methods are used to send a message to the LSP client.
void call(StringRef Method, llvm::json::Value Params, llvm::json::Value Id)
This class models an LSP error as an llvm::Error.
Definition Protocol.h:79
static void error(const char *Fmt, Ts &&...Vals)
Definition Logging.h:38
static void debug(const char *Fmt, Ts &&...Vals)
Initiate a log message at various severity levels.
Definition Logging.h:32
static void info(const char *Fmt, Ts &&...Vals)
Definition Logging.h:35
A handler used to process the incoming transport messages.
Definition Transport.h:157
bool onNotify(StringRef Method, llvm::json::Value Value)
Definition Transport.cpp:86
bool onCall(StringRef Method, llvm::json::Value Params, llvm::json::Value Id)
bool onReply(llvm::json::Value Id, llvm::Expected< llvm::json::Value > Result)
A raw_ostream that writes to an SmallVector or SmallString.
LLVM_ABI llvm::Expected< Value > parse(llvm::StringRef JSON)
Parses the provided JSON source, or returns a ParseError.
Definition JSON.cpp:675
This is an optimization pass for GlobalISel generic memory operations.
bool succeeded(LogicalResult Result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
bool failed(LogicalResult Result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:98
Error handleErrors(Error E, HandlerTs &&... Hs)
Pass the ErrorInfo(s) contained in E to their respective handlers.
Definition Error.h:967
LogicalResult failure(bool IsFailure=true)
Utility function to generate a LogicalResult.
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
static std::string debugString(T &&Op)
Definition Transport.h:30
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
@ Other
Any other memory.
Definition ModRef.h:68
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1849
LLVM_ABI bool getAsUnsignedInteger(StringRef Str, unsigned Radix, unsigned long long &Result)
Helper functions for StringRef::getAsInteger.
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:111
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1083
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:851
This class represents an efficient way to signal success or failure.