12 #include "llvm/ADT/SmallString.h"
13 #include "llvm/Support/Errno.h"
14 #include "llvm/Support/Error.h"
16 #include <system_error>
33 Reply(
const llvm::json::Value &
id, StringRef method,
JSONTransport &transport,
34 std::mutex &transportOutputMutex);
36 Reply &operator=(Reply &&) =
delete;
37 Reply(
const Reply &) =
delete;
38 Reply &operator=(
const Reply &) =
delete;
44 std::atomic<bool> replied = {
false};
47 std::mutex &transportOutputMutex;
51 Reply::Reply(
const llvm::json::Value &
id, llvm::StringRef method,
53 : id(id), transport(&transport),
54 transportOutputMutex(transportOutputMutex) {}
56 Reply::Reply(Reply &&other)
57 : replied(other.replied.load()), id(std::move(other.id)),
58 transport(other.transport),
59 transportOutputMutex(other.transportOutputMutex) {
60 other.transport =
nullptr;
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!");
69 assert(transport &&
"expected valid transport to reply to");
71 std::lock_guard<std::mutex> transportLock(transportOutputMutex);
73 Logger::info(
"--> reply:{0}({1})", method,
id);
74 transport->reply(std::move(
id), std::move(reply));
76 llvm::Error error = reply.takeError();
77 Logger::info(
"--> reply:{0}({1})", method,
id, error);
78 transport->reply(std::move(
id), std::move(error));
86 bool MessageHandler::onNotify(llvm::StringRef method, llvm::json::Value value) {
94 auto it = notificationHandlers.find(
method);
95 if (it != notificationHandlers.end())
96 it->second(std::move(value));
102 llvm::json::Value
id) {
105 Reply reply(
id,
method, transport, transportOutputMutex);
107 auto it = methodHandlers.find(
method);
108 if (it != methodHandlers.end()) {
109 it->second(std::move(params), std::move(reply));
111 reply(llvm::make_error<LSPError>(
"method not found: " +
method.str(),
124 "received a reply with ID {0}, but there was no such call",
id);
126 llvm::consumeError(result.takeError());
131 replyHandler(std::move(result));
133 replyHandler(result.takeError());
145 auto handlerFn = [&](
const LSPError &lspError) -> llvm::Error {
146 message = lspError.message;
147 code = lspError.code;
150 if (llvm::Error unhandled = llvm::handleErrors(std::move(error), handlerFn))
153 return llvm::json::Object{
154 {
"message", std::move(message)},
155 {
"code", int64_t(code)},
161 StringRef msg = o.getString(
"message").value_or(
"Unspecified error");
162 if (std::optional<int64_t> code = o.getInteger(
"code"))
163 return llvm::make_error<LSPError>(msg.str(),
ErrorCode(*code));
164 return llvm::make_error<llvm::StringError>(llvm::inconvertibleErrorCode(),
169 sendMessage(llvm::json::Object{
172 {
"params", std::move(params)},
176 llvm::json::Value
id) {
177 sendMessage(llvm::json::Object{
179 {
"id", std::move(
id)},
181 {
"params", std::move(params)},
187 return sendMessage(llvm::json::Object{
189 {
"id", std::move(
id)},
190 {
"result", std::move(*result)},
194 sendMessage(llvm::json::Object{
196 {
"id", std::move(
id)},
205 return llvm::errorCodeToError(
206 std::error_code(errno, std::system_category()));
211 if (!handleMessage(std::move(*doc), handler))
218 return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
221 void JSONTransport::sendMessage(llvm::json::Value msg) {
222 outputBuffer.clear();
223 llvm::raw_svector_ostream os(outputBuffer);
224 os << llvm::formatv(prettyOutput ?
"{0:2}\n" :
"{0}", msg);
225 out <<
"Content-Length: " << outputBuffer.size() <<
"\r\n\r\n"
231 bool JSONTransport::handleMessage(llvm::json::Value msg,
234 llvm::json::Object *
object = msg.getAsObject();
236 object->getString(
"jsonrpc") != std::optional<StringRef>(
"2.0"))
240 std::optional<llvm::json::Value> id;
241 if (llvm::json::Value *i = object->get(
"id"))
243 std::optional<StringRef> method =
object->getString(
"method");
249 if (
auto *err = object->getObject(
"error"))
252 llvm::json::Value result =
nullptr;
253 if (llvm::json::Value *r = object->get(
"result"))
254 result = std::move(*r);
255 return handler.
onReply(std::move(*
id), std::move(result));
259 llvm::json::Value params =
nullptr;
260 if (llvm::json::Value *p = object->get(
"params"))
261 params = std::move(*p);
264 return handler.
onCall(*method, std::move(params), std::move(*
id));
265 return handler.
onNotify(*method, std::move(params));
273 static constexpr
int bufSize = 128;
277 out.resize_for_overwrite(size + bufSize);
278 if (!std::fgets(&out[size], bufSize, in))
285 size_t read = std::strlen(&out[size]);
286 if (read > 0 && out[size + read - 1] ==
'\n') {
287 out.resize(size + read);
297 LogicalResult JSONTransport::readStandardMessage(std::string &json) {
300 unsigned long long contentLength = 0;
307 StringRef lineRef = line;
308 if (lineRef.consume_front(
"Content-Length: ")) {
309 llvm::getAsUnsignedInteger(lineRef.trim(), 0, contentLength);
310 }
else if (!lineRef.trim().empty()) {
320 if (contentLength == 0 || contentLength > 1 << 30)
323 json.resize(contentLength);
324 for (
size_t pos = 0, read; pos < contentLength; pos += read) {
325 read = std::fread(&json[pos], 1, contentLength - pos, in);
343 LogicalResult JSONTransport::readDelimitedMessage(std::string &json) {
347 StringRef lineRef = line.str().trim();
348 if (lineRef.startswith(
"//")) {
350 if (lineRef ==
"// -----")
static std::string toString(bytecode::Section::ID sectionID)
Stringify the given section ID.
static llvm::json::Object encodeError(llvm::Error error)
Encode the given error as a JSON object.
llvm::Error decodeError(const llvm::json::Object &o)
Decode the given JSON object into an error.
LogicalResult readLine(std::FILE *in, SmallVectorImpl< char > &out)
Tries to read a line up to and including .
A transport class that performs the JSON-RPC communication with the LSP client.
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)
llvm::Error run(MessageHandler &handler)
Start executing the JSON-RPC transport.
void reply(llvm::json::Value id, llvm::Expected< llvm::json::Value > result)
This class models an LSP error as an llvm::Error.
static void debug(const char *fmt, Ts &&...vals)
Initiate a log message at various severity levels.
static void info(const char *fmt, Ts &&...vals)
static void error(const char *fmt, Ts &&...vals)
A handler used to process the incoming transport messages.
bool onCall(StringRef method, llvm::json::Value params, llvm::json::Value id)
void method(llvm::StringLiteral method, ThisT *thisPtr, void(ThisT::*handler)(const Param &, Callback< Result >))
bool onReply(llvm::json::Value id, llvm::Expected< llvm::json::Value > result)
bool onNotify(StringRef method, llvm::json::Value value)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
This header declares functions that assist transformations in the MemRef dialect.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
This class represents an efficient way to signal success or failure.