[Mlir-commits] [mlir] [mlir-lsp] Abstract input and output of the `JSONTransport` (PR #129320)
Nikolay Panchenko
llvmlistbot at llvm.org
Thu Mar 6 10:19:41 PST 2025
https://github.com/npanchen updated https://github.com/llvm/llvm-project/pull/129320
>From dcb202d32bc3d93084d37be5269d166a9c17926c Mon Sep 17 00:00:00 2001
From: Kolya Panchenko <npanchen at modular.com>
Date: Fri, 28 Feb 2025 15:34:56 -0500
Subject: [PATCH 1/2] [mlir-lsp] Abstract input and output of the
`JSONTransport`
The patch abstracts sending and receiving json messages of
`JSONTransport` to allow custom implementation of them. For example, one
concrete implementation can use pipes without a need to convert file
descriptor to a `FILE` object.
---
.../mlir/Tools/lsp-server-support/Transport.h | 99 +++++++++++++++----
.../Tools/lsp-server-support/Transport.cpp | 22 +++--
2 files changed, 90 insertions(+), 31 deletions(-)
diff --git a/mlir/include/mlir/Tools/lsp-server-support/Transport.h b/mlir/include/mlir/Tools/lsp-server-support/Transport.h
index 6843bc76ab9dc..78f9d48e4153a 100644
--- a/mlir/include/mlir/Tools/lsp-server-support/Transport.h
+++ b/mlir/include/mlir/Tools/lsp-server-support/Transport.h
@@ -43,14 +43,86 @@ enum JSONStreamStyle {
Delimited
};
+/// An abstract class used by the JSONTransport to read JSON message.
+class JSONTransportInput {
+public:
+ explicit JSONTransportInput(JSONStreamStyle style = JSONStreamStyle::Standard)
+ : style(style) {}
+ virtual ~JSONTransportInput() = default;
+
+ virtual bool getError() const = 0;
+ virtual bool isEndOfInput() const = 0;
+
+ /// Read in a message from the input stream.
+ virtual LogicalResult readMessage(std::string &json) {
+ return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
+ : readStandardMessage(json);
+ }
+ virtual LogicalResult readDelimitedMessage(std::string &json) = 0;
+ virtual LogicalResult readStandardMessage(std::string &json) = 0;
+
+private:
+ /// The JSON stream style to use.
+ JSONStreamStyle style;
+};
+
+/// An abstract class used by the JSONTransport to write JSON messages.
+class JSONTransportOutput {
+public:
+ explicit JSONTransportOutput() = default;
+ virtual ~JSONTransportOutput() = default;
+
+ virtual void sendMessage(const llvm::json::Value &msg) = 0;
+};
+
+/// Concrete implementation of the JSONTransportInput that reads from a file.
+class JSONTransportInputOverFile : public JSONTransportInput {
+public:
+ explicit JSONTransportInputOverFile(
+ std::FILE *in, JSONStreamStyle style = JSONStreamStyle::Standard)
+ : JSONTransportInput(style), in(in) {}
+
+ bool getError() const final { return ferror(in); }
+ bool isEndOfInput() const final { return feof(in); }
+
+ LogicalResult readDelimitedMessage(std::string &json) final;
+ LogicalResult readStandardMessage(std::string &json) final;
+
+private:
+ std::FILE *in;
+};
+
+/// Concrete implementation of the JSONTransportOutput that writes to a stream.
+class JSONTransportOutputOverStream : public JSONTransportOutput {
+public:
+ explicit JSONTransportOutputOverStream(raw_ostream &out,
+ bool prettyOutput = false)
+ : JSONTransportOutput(), out(out), prettyOutput(prettyOutput) {}
+
+ /// Writes the given message to the output stream.
+ void sendMessage(const llvm::json::Value &msg) final;
+
+private:
+ SmallVector<char, 0> outputBuffer;
+ raw_ostream &out;
+ /// If the output JSON should be formatted for easier readability.
+ bool prettyOutput;
+};
+
/// A transport class that performs the JSON-RPC communication with the LSP
/// client.
class JSONTransport {
public:
+ JSONTransport(std::unique_ptr<JSONTransportInput> in,
+ std::unique_ptr<JSONTransportOutput> out)
+ : in(std::move(in)), out(std::move(out)) {}
+
JSONTransport(std::FILE *in, raw_ostream &out,
JSONStreamStyle style = JSONStreamStyle::Standard,
bool prettyOutput = false)
- : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
+ : in(std::make_unique<JSONTransportInputOverFile>(in, style)),
+ out(std::make_unique<JSONTransportOutputOverStream>(out,
+ prettyOutput)) {}
/// The following methods are used to send a message to the LSP client.
void notify(StringRef method, llvm::json::Value params);
@@ -60,30 +132,15 @@ class JSONTransport {
/// Start executing the JSON-RPC transport.
llvm::Error run(MessageHandler &handler);
-private:
/// Dispatches the given incoming json message to the message handler.
bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
- /// Writes the given message to the output stream.
- void sendMessage(llvm::json::Value msg);
- /// Read in a message from the input stream.
- LogicalResult readMessage(std::string &json) {
- return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
- : readStandardMessage(json);
- }
- LogicalResult readDelimitedMessage(std::string &json);
- LogicalResult readStandardMessage(std::string &json);
+private:
+ /// The input to read a message from.
+ std::unique_ptr<JSONTransportInput> in;
- /// An output buffer used when building output messages.
- SmallVector<char, 0> outputBuffer;
- /// The input file stream.
- std::FILE *in;
- /// The output file stream.
- raw_ostream &out;
- /// The JSON stream style to use.
- JSONStreamStyle style;
- /// If the output JSON should be formatted for easier readability.
- bool prettyOutput;
+ /// The output to send a messages to.
+ std::unique_ptr<JSONTransportOutput> out;
};
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Tools/lsp-server-support/Transport.cpp b/mlir/lib/Tools/lsp-server-support/Transport.cpp
index ad8308f69aead..d6806bf5cfe13 100644
--- a/mlir/lib/Tools/lsp-server-support/Transport.cpp
+++ b/mlir/lib/Tools/lsp-server-support/Transport.cpp
@@ -175,7 +175,7 @@ llvm::Error decodeError(const llvm::json::Object &o) {
}
void JSONTransport::notify(StringRef method, llvm::json::Value params) {
- sendMessage(llvm::json::Object{
+ out->sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"method", method},
{"params", std::move(params)},
@@ -183,7 +183,7 @@ void JSONTransport::notify(StringRef method, llvm::json::Value params) {
}
void JSONTransport::call(StringRef method, llvm::json::Value params,
llvm::json::Value id) {
- sendMessage(llvm::json::Object{
+ out->sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"method", method},
@@ -193,14 +193,14 @@ void JSONTransport::call(StringRef method, llvm::json::Value params,
void JSONTransport::reply(llvm::json::Value id,
llvm::Expected<llvm::json::Value> result) {
if (result) {
- return sendMessage(llvm::json::Object{
+ return out->sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"result", std::move(*result)},
});
}
- sendMessage(llvm::json::Object{
+ out->sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"error", encodeError(result.takeError())},
@@ -209,13 +209,13 @@ void JSONTransport::reply(llvm::json::Value id,
llvm::Error JSONTransport::run(MessageHandler &handler) {
std::string json;
- while (!feof(in)) {
- if (ferror(in)) {
+ while (!in->isEndOfInput()) {
+ if (in->getError()) {
return llvm::errorCodeToError(
std::error_code(errno, std::system_category()));
}
- if (succeeded(readMessage(json))) {
+ if (succeeded(in->readMessage(json))) {
if (llvm::Expected<llvm::json::Value> doc = llvm::json::parse(json)) {
if (!handleMessage(std::move(*doc), handler))
return llvm::Error::success();
@@ -227,7 +227,7 @@ llvm::Error JSONTransport::run(MessageHandler &handler) {
return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
}
-void JSONTransport::sendMessage(llvm::json::Value msg) {
+void JSONTransportOutputOverStream::sendMessage(const llvm::json::Value &msg) {
outputBuffer.clear();
llvm::raw_svector_ostream os(outputBuffer);
os << llvm::formatv(prettyOutput ? "{0:2}\n" : "{0}", msg);
@@ -303,7 +303,8 @@ LogicalResult readLine(std::FILE *in, SmallVectorImpl<char> &out) {
// Returns std::nullopt when:
// - ferror(), feof(), or shutdownRequested() are set.
// - Content-Length is missing or empty (protocol error)
-LogicalResult JSONTransport::readStandardMessage(std::string &json) {
+LogicalResult
+JSONTransportInputOverFile::readStandardMessage(std::string &json) {
// A Language Server Protocol message starts with a set of HTTP headers,
// delimited by \r\n, and terminated by an empty line (\r\n).
unsigned long long contentLength = 0;
@@ -349,7 +350,8 @@ LogicalResult JSONTransport::readStandardMessage(std::string &json) {
/// This is a testing path, so favor simplicity over performance here.
/// When returning failure: feof(), ferror(), or shutdownRequested() will be
/// set.
-LogicalResult JSONTransport::readDelimitedMessage(std::string &json) {
+LogicalResult
+JSONTransportInputOverFile::readDelimitedMessage(std::string &json) {
json.clear();
llvm::SmallString<128> line;
while (succeeded(readLine(in, line))) {
>From b62319d7200931a25f1c75aa4bcf076e8b733d1a Mon Sep 17 00:00:00 2001
From: Kolya Panchenko <npanchen at modular.com>
Date: Mon, 3 Mar 2025 10:02:18 -0800
Subject: [PATCH 2/2] Addressed comments
---
.../mlir/Tools/lsp-server-support/Transport.h | 54 ++++++-------------
.../Tools/lsp-server-support/Transport.cpp | 14 ++---
2 files changed, 23 insertions(+), 45 deletions(-)
diff --git a/mlir/include/mlir/Tools/lsp-server-support/Transport.h b/mlir/include/mlir/Tools/lsp-server-support/Transport.h
index 78f9d48e4153a..0010a475fedd2 100644
--- a/mlir/include/mlir/Tools/lsp-server-support/Transport.h
+++ b/mlir/include/mlir/Tools/lsp-server-support/Transport.h
@@ -50,11 +50,11 @@ class JSONTransportInput {
: style(style) {}
virtual ~JSONTransportInput() = default;
- virtual bool getError() const = 0;
+ virtual bool hasError() const = 0;
virtual bool isEndOfInput() const = 0;
/// Read in a message from the input stream.
- virtual LogicalResult readMessage(std::string &json) {
+ LogicalResult readMessage(std::string &json) {
return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
: readStandardMessage(json);
}
@@ -66,15 +66,6 @@ class JSONTransportInput {
JSONStreamStyle style;
};
-/// An abstract class used by the JSONTransport to write JSON messages.
-class JSONTransportOutput {
-public:
- explicit JSONTransportOutput() = default;
- virtual ~JSONTransportOutput() = default;
-
- virtual void sendMessage(const llvm::json::Value &msg) = 0;
-};
-
/// Concrete implementation of the JSONTransportInput that reads from a file.
class JSONTransportInputOverFile : public JSONTransportInput {
public:
@@ -82,7 +73,7 @@ class JSONTransportInputOverFile : public JSONTransportInput {
std::FILE *in, JSONStreamStyle style = JSONStreamStyle::Standard)
: JSONTransportInput(style), in(in) {}
- bool getError() const final { return ferror(in); }
+ bool hasError() const final { return ferror(in); }
bool isEndOfInput() const final { return feof(in); }
LogicalResult readDelimitedMessage(std::string &json) final;
@@ -92,37 +83,19 @@ class JSONTransportInputOverFile : public JSONTransportInput {
std::FILE *in;
};
-/// Concrete implementation of the JSONTransportOutput that writes to a stream.
-class JSONTransportOutputOverStream : public JSONTransportOutput {
-public:
- explicit JSONTransportOutputOverStream(raw_ostream &out,
- bool prettyOutput = false)
- : JSONTransportOutput(), out(out), prettyOutput(prettyOutput) {}
-
- /// Writes the given message to the output stream.
- void sendMessage(const llvm::json::Value &msg) final;
-
-private:
- SmallVector<char, 0> outputBuffer;
- raw_ostream &out;
- /// If the output JSON should be formatted for easier readability.
- bool prettyOutput;
-};
-
/// A transport class that performs the JSON-RPC communication with the LSP
/// client.
class JSONTransport {
public:
- JSONTransport(std::unique_ptr<JSONTransportInput> in,
- std::unique_ptr<JSONTransportOutput> out)
- : in(std::move(in)), out(std::move(out)) {}
+ JSONTransport(std::unique_ptr<JSONTransportInput> in, raw_ostream &out,
+ bool prettyOutput = false)
+ : in(std::move(in)), out(out), prettyOutput(prettyOutput) {}
JSONTransport(std::FILE *in, raw_ostream &out,
JSONStreamStyle style = JSONStreamStyle::Standard,
bool prettyOutput = false)
- : in(std::make_unique<JSONTransportInputOverFile>(in, style)),
- out(std::make_unique<JSONTransportOutputOverStream>(out,
- prettyOutput)) {}
+ : in(std::make_unique<JSONTransportInputOverFile>(in, style)), out(out),
+ prettyOutput(prettyOutput) {}
/// The following methods are used to send a message to the LSP client.
void notify(StringRef method, llvm::json::Value params);
@@ -132,15 +105,20 @@ class JSONTransport {
/// Start executing the JSON-RPC transport.
llvm::Error run(MessageHandler &handler);
+private:
/// Dispatches the given incoming json message to the message handler.
bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
+ /// Writes the given message to the output stream.
+ void sendMessage(llvm::json::Value msg);
private:
/// The input to read a message from.
std::unique_ptr<JSONTransportInput> in;
-
- /// The output to send a messages to.
- std::unique_ptr<JSONTransportOutput> out;
+ SmallVector<char, 0> outputBuffer;
+ /// The output file stream.
+ raw_ostream &out;
+ /// If the output JSON should be formatted for easier readability.
+ bool prettyOutput;
};
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Tools/lsp-server-support/Transport.cpp b/mlir/lib/Tools/lsp-server-support/Transport.cpp
index d6806bf5cfe13..d0863ba0ae087 100644
--- a/mlir/lib/Tools/lsp-server-support/Transport.cpp
+++ b/mlir/lib/Tools/lsp-server-support/Transport.cpp
@@ -175,7 +175,7 @@ llvm::Error decodeError(const llvm::json::Object &o) {
}
void JSONTransport::notify(StringRef method, llvm::json::Value params) {
- out->sendMessage(llvm::json::Object{
+ sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"method", method},
{"params", std::move(params)},
@@ -183,7 +183,7 @@ void JSONTransport::notify(StringRef method, llvm::json::Value params) {
}
void JSONTransport::call(StringRef method, llvm::json::Value params,
llvm::json::Value id) {
- out->sendMessage(llvm::json::Object{
+ sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"method", method},
@@ -193,14 +193,14 @@ void JSONTransport::call(StringRef method, llvm::json::Value params,
void JSONTransport::reply(llvm::json::Value id,
llvm::Expected<llvm::json::Value> result) {
if (result) {
- return out->sendMessage(llvm::json::Object{
+ return sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"result", std::move(*result)},
});
}
- out->sendMessage(llvm::json::Object{
+ sendMessage(llvm::json::Object{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"error", encodeError(result.takeError())},
@@ -210,7 +210,7 @@ void JSONTransport::reply(llvm::json::Value id,
llvm::Error JSONTransport::run(MessageHandler &handler) {
std::string json;
while (!in->isEndOfInput()) {
- if (in->getError()) {
+ if (in->hasError()) {
return llvm::errorCodeToError(
std::error_code(errno, std::system_category()));
}
@@ -227,7 +227,7 @@ llvm::Error JSONTransport::run(MessageHandler &handler) {
return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
}
-void JSONTransportOutputOverStream::sendMessage(const llvm::json::Value &msg) {
+void JSONTransport::sendMessage(llvm::json::Value msg) {
outputBuffer.clear();
llvm::raw_svector_ostream os(outputBuffer);
os << llvm::formatv(prettyOutput ? "{0:2}\n" : "{0}", msg);
@@ -310,7 +310,7 @@ JSONTransportInputOverFile::readStandardMessage(std::string &json) {
unsigned long long contentLength = 0;
llvm::SmallString<128> line;
while (true) {
- if (feof(in) || ferror(in) || failed(readLine(in, line)))
+ if (feof(in) || hasError() || failed(readLine(in, line)))
return failure();
// Content-Length is a mandatory header, and the only one we handle.
More information about the Mlir-commits
mailing list