blob: 20a2cbabe406ec0d7cd2b4cbb2f61f6607cea636 [file] [log] [blame]
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +00001//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
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
10#include "JSONRPCDispatcher.h"
Kadir Cetinkaya689bf932018-08-24 13:09:41 +000011#include "Cancellation.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000012#include "ProtocolHandlers.h"
Sam McCall8567cb32017-11-02 09:21:51 +000013#include "Trace.h"
Sam McCalldc8f3cf2018-10-17 07:32:05 +000014#include "Transport.h"
Sam McCall45be5cf2018-09-13 12:58:36 +000015#include "llvm/ADT/ScopeExit.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000016#include "llvm/ADT/SmallString.h"
Sam McCall94362c62017-11-07 14:45:31 +000017#include "llvm/ADT/StringExtras.h"
Sam McCalla90f2572018-02-16 16:41:42 +000018#include "llvm/Support/Chrono.h"
Sam McCall27a07cf2018-06-05 09:34:46 +000019#include "llvm/Support/Errno.h"
Sam McCallbed58852018-07-11 10:35:11 +000020#include "llvm/Support/FormatVariadic.h"
Sam McCalld20d7982018-07-09 14:25:59 +000021#include "llvm/Support/JSON.h"
Sam McCall45be5cf2018-09-13 12:58:36 +000022#include "llvm/Support/ScopedPrinter.h"
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000023#include "llvm/Support/SourceMgr.h"
Ilya Biryukov687b92a2017-05-16 15:23:55 +000024#include <istream>
25
Sam McCalld20d7982018-07-09 14:25:59 +000026using namespace llvm;
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000027using namespace clang;
28using namespace clangd;
29
Ilya Biryukov940901e2017-12-13 12:51:22 +000030namespace {
Sam McCalld20d7982018-07-09 14:25:59 +000031static Key<json::Value> RequestID;
Sam McCalldc8f3cf2018-10-17 07:32:05 +000032static Key<Transport *> CurrentTransport;
Sam McCall1b475a12018-01-26 09:00:30 +000033
Fangrui Song445bdd12018-09-05 08:01:37 +000034// When tracing, we trace a request and attach the response in reply().
Sam McCall1b475a12018-01-26 09:00:30 +000035// Because the Span isn't available, we find the current request using Context.
36class RequestSpan {
Sam McCalld20d7982018-07-09 14:25:59 +000037 RequestSpan(llvm::json::Object *Args) : Args(Args) {}
Sam McCall1b475a12018-01-26 09:00:30 +000038 std::mutex Mu;
Sam McCalld20d7982018-07-09 14:25:59 +000039 llvm::json::Object *Args;
Sam McCall24f0fa32018-01-26 11:23:33 +000040 static Key<std::unique_ptr<RequestSpan>> RSKey;
Sam McCall1b475a12018-01-26 09:00:30 +000041
42public:
43 // Return a context that's aware of the enclosing request, identified by Span.
44 static Context stash(const trace::Span &Span) {
Sam McCalld1a7a372018-01-31 13:40:48 +000045 return Context::current().derive(
46 RSKey, std::unique_ptr<RequestSpan>(new RequestSpan(Span.Args)));
Sam McCall1b475a12018-01-26 09:00:30 +000047 }
48
49 // If there's an enclosing request and the tracer is interested, calls \p F
Sam McCalld20d7982018-07-09 14:25:59 +000050 // with a json::Object where request info can be added.
Sam McCalld1a7a372018-01-31 13:40:48 +000051 template <typename Func> static void attach(Func &&F) {
52 auto *RequestArgs = Context::current().get(RSKey);
Sam McCall1b475a12018-01-26 09:00:30 +000053 if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
54 return;
55 std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
56 F(*(*RequestArgs)->Args);
57 }
58};
Sam McCall24f0fa32018-01-26 11:23:33 +000059Key<std::unique_ptr<RequestSpan>> RequestSpan::RSKey;
Ilya Biryukov940901e2017-12-13 12:51:22 +000060} // namespace
61
Sam McCallbed58852018-07-11 10:35:11 +000062void JSONOutput::log(Logger::Level Level,
63 const llvm::formatv_object_base &Message) {
64 if (Level < MinLevel)
65 return;
Sam McCalla90f2572018-02-16 16:41:42 +000066 llvm::sys::TimePoint<> Timestamp = std::chrono::system_clock::now();
Sam McCalld1a7a372018-01-31 13:40:48 +000067 trace::log(Message);
Benjamin Kramere14bd422017-02-15 16:44:11 +000068 std::lock_guard<std::mutex> Guard(StreamMutex);
Sam McCallbed58852018-07-11 10:35:11 +000069 Logs << llvm::formatv("{0}[{1:%H:%M:%S.%L}] {2}\n", indicator(Level),
70 Timestamp, Message);
Benjamin Kramere14bd422017-02-15 16:44:11 +000071 Logs.flush();
72}
73
Sam McCalld20d7982018-07-09 14:25:59 +000074void clangd::reply(json::Value &&Result) {
Kadir Cetinkaya689bf932018-08-24 13:09:41 +000075 auto ID = getRequestId();
Sam McCalldd0566b2017-11-06 15:40:30 +000076 if (!ID) {
Sam McCallbed58852018-07-11 10:35:11 +000077 elog("Attempted to reply to a notification!");
Sam McCall8a5dded2017-10-12 13:29:58 +000078 return;
79 }
Sam McCalld20d7982018-07-09 14:25:59 +000080 RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; });
Sam McCall8d7760c2018-07-12 11:52:18 +000081 log("--> reply({0})", *ID);
Sam McCalld1a7a372018-01-31 13:40:48 +000082 Context::current()
Sam McCalldc8f3cf2018-10-17 07:32:05 +000083 .getExisting(CurrentTransport)
84 ->reply(std::move(*ID), std::move(Result));
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000085}
86
Ilya Biryukov74f26552018-07-26 12:05:31 +000087void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) {
88 elog("Error {0}: {1}", static_cast<int>(Code), Message);
Sam McCalld20d7982018-07-09 14:25:59 +000089 RequestSpan::attach([&](json::Object &Args) {
Ilya Biryukov74f26552018-07-26 12:05:31 +000090 Args["Error"] = json::Object{{"code", static_cast<int>(Code)},
Sam McCalld20d7982018-07-09 14:25:59 +000091 {"message", Message.str()}};
Sam McCall1b475a12018-01-26 09:00:30 +000092 });
Ilya Biryukov940901e2017-12-13 12:51:22 +000093
Kadir Cetinkaya689bf932018-08-24 13:09:41 +000094 if (auto ID = getRequestId()) {
Sam McCall8d7760c2018-07-12 11:52:18 +000095 log("--> reply({0}) error: {1}", *ID, Message);
Sam McCalld1a7a372018-01-31 13:40:48 +000096 Context::current()
Sam McCalldc8f3cf2018-10-17 07:32:05 +000097 .getExisting(CurrentTransport)
98 ->reply(std::move(*ID), make_error<LSPError>(Message, Code));
Sam McCall8a5dded2017-10-12 13:29:58 +000099 }
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000100}
101
Kadir Cetinkaya689bf932018-08-24 13:09:41 +0000102void clangd::replyError(Error E) {
103 handleAllErrors(std::move(E),
104 [](const CancelledError &TCE) {
105 replyError(ErrorCode::RequestCancelled, TCE.message());
106 },
107 [](const ErrorInfoBase &EIB) {
108 replyError(ErrorCode::InvalidParams, EIB.message());
109 });
110}
111
Sam McCalld20d7982018-07-09 14:25:59 +0000112void clangd::call(StringRef Method, json::Value &&Params) {
Sam McCalld20d7982018-07-09 14:25:59 +0000113 RequestSpan::attach([&](json::Object &Args) {
114 Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}};
Sam McCall1b475a12018-01-26 09:00:30 +0000115 });
Sam McCall8d7760c2018-07-12 11:52:18 +0000116 // FIXME: Generate/Increment IDs for every request so that we can get proper
117 // replies once we need to.
118 auto ID = 1;
119 log("--> {0}({1})", Method, ID);
Sam McCalld1a7a372018-01-31 13:40:48 +0000120 Context::current()
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000121 .getExisting(CurrentTransport)
122 ->call(Method, std::move(Params), ID);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000123}
124
Sam McCall45be5cf2018-09-13 12:58:36 +0000125JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler)
126 : UnknownHandler(std::move(UnknownHandler)) {
127 registerHandler("$/cancelRequest", [this](const json::Value &Params) {
128 if (auto *O = Params.getAsObject())
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000129 if (auto *ID = O->get("id")) {
130 cancelRequest(*ID);
131 return true;
132 }
Sam McCall45be5cf2018-09-13 12:58:36 +0000133 log("Bad cancellation request: {0}", Params);
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000134 return true;
Sam McCall45be5cf2018-09-13 12:58:36 +0000135 });
136}
137
Sam McCall8a5dded2017-10-12 13:29:58 +0000138void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000139 assert(!Handlers.count(Method) && "Handler already registered!");
140 Handlers[Method] = std::move(H);
141}
142
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000143bool JSONRPCDispatcher::onCall(StringRef Method, json::Value Params,
144 json::Value ID) {
145 log("<-- {0}({1})", Method, ID);
146 auto I = Handlers.find(Method);
Sam McCallec109022017-11-28 09:37:43 +0000147 auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
Ilya Biryukov940901e2017-12-13 12:51:22 +0000148
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000149 // Create a Context that contains request information.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000150 WithContextValue WithID(RequestID, ID);
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000151
152 // Create a tracing Span covering the whole request lifetime.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000153 trace::Span Tracer(Method);
154 SPAN_ATTACH(Tracer, "ID", ID);
Sam McCall1b475a12018-01-26 09:00:30 +0000155 SPAN_ATTACH(Tracer, "Params", Params);
Ilya Biryukov940901e2017-12-13 12:51:22 +0000156
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000157 // Calls can be canceled by the client. Add cancellation context.
158 WithContext WithCancel(cancelableRequestContext(ID));
Sam McCall45be5cf2018-09-13 12:58:36 +0000159
Sam McCall1b475a12018-01-26 09:00:30 +0000160 // Stash a reference to the span args, so later calls can add metadata.
Sam McCalld1a7a372018-01-31 13:40:48 +0000161 WithContext WithRequestSpan(RequestSpan::stash(Tracer));
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000162 return Handler(std::move(Params));
163}
164
165bool JSONRPCDispatcher::onNotify(StringRef Method, json::Value Params) {
166 log("<-- {0}", Method);
167 auto I = Handlers.find(Method);
168 auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
169
170 // Create a tracing Span covering the whole request lifetime.
171 trace::Span Tracer(Method);
172 SPAN_ATTACH(Tracer, "Params", Params);
173
174 // Stash a reference to the span args, so later calls can add metadata.
175 WithContext WithRequestSpan(RequestSpan::stash(Tracer));
176 return Handler(std::move(Params));
177}
178
179bool JSONRPCDispatcher::onReply(json::Value ID, Expected<json::Value> Result) {
180 // We ignore replies, just log them.
181 if (Result)
182 log("<-- reply({0})", ID);
183 else
184 log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError()));
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000185 return true;
186}
Ilya Biryukovafb55542017-05-16 14:40:30 +0000187
Sam McCall45be5cf2018-09-13 12:58:36 +0000188// We run cancelable requests in a context that does two things:
189// - allows cancellation using RequestCancelers[ID]
190// - cleans up the entry in RequestCancelers when it's no longer needed
191// If a client reuses an ID, the last one wins and the first cannot be canceled.
192Context JSONRPCDispatcher::cancelableRequestContext(const json::Value &ID) {
193 auto Task = cancelableTask();
194 auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key.
195 auto Cookie = NextRequestCookie++; // No lock, only called on main thread.
196 {
197 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
198 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
199 }
200 // When the request ends, we can clean up the entry we just added.
201 // The cookie lets us check that it hasn't been overwritten due to ID reuse.
202 return Task.first.derive(make_scope_exit([this, StrID, Cookie] {
203 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
204 auto It = RequestCancelers.find(StrID);
205 if (It != RequestCancelers.end() && It->second.second == Cookie)
206 RequestCancelers.erase(It);
207 }));
208}
209
210void JSONRPCDispatcher::cancelRequest(const json::Value &ID) {
211 auto StrID = llvm::to_string(ID);
212 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
213 auto It = RequestCancelers.find(StrID);
214 if (It != RequestCancelers.end())
215 It->second.first(); // Invoke the canceler.
216}
217
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000218llvm::Error JSONRPCDispatcher::runLanguageServerLoop(Transport &Transport) {
219 // Propagate transport to all handlers so they can reply.
220 WithContextValue WithTransport(CurrentTransport, &Transport);
221 return Transport.loop(*this);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000222}
Kadir Cetinkaya689bf932018-08-24 13:09:41 +0000223
224const json::Value *clangd::getRequestId() {
225 return Context::current().get(RequestID);
226}