Skip to content

Commit 24ae573

Browse files
LSP: Adds TCP transport (optionally *ONLY* available in debug builds & disabled by default).
1 parent 0926053 commit 24ae573

File tree

4 files changed

+200
-1
lines changed

4 files changed

+200
-1
lines changed

solc/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,21 @@ set(
44
main.cpp
55
)
66

7+
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
8+
# Since we do not want to depend on networking code on production binaries,
9+
# this feature is only available when creating a debug build.
10+
# the LSP's TCP listener is exclusively used more convenient debugging.
11+
option(SOLC_LSP_TCP "Solidity LSP: Enables TCP listener support (should only be eanbled for debugging purposes)." OFF)
12+
if(SOLC_LSP_TCP)
13+
set(SOLC_DEFINITIONS SOLC_LSP_TCP=1)
14+
list(APPEND sources LSPTCPTransport.cpp LSPTCPTransport.h)
15+
set(SOLC_LSP_LIBS pthread)
16+
endif()
17+
endif()
18+
719
add_executable(solc ${sources})
8-
target_link_libraries(solc PRIVATE solidity Boost::boost Boost::program_options)
20+
target_link_libraries(solc PRIVATE solidity Boost::boost Boost::program_options ${SOLC_LSP_LIBS})
21+
target_compile_definitions(solc PRIVATE ${SOLC_DEFINITIONS})
922

1023
include(GNUInstallDirs)
1124
install(TARGETS solc DESTINATION "${CMAKE_INSTALL_BINDIR}")

solc/CommandLineInterface.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
*/
2424
#include <solc/CommandLineInterface.h>
2525

26+
#if defined(SOLC_LSP_TCP)
27+
#include <solc/LSPTCPTransport.h>
28+
#endif
29+
2630
#include "solidity/BuildInfo.h"
2731
#include "license.h"
2832

@@ -938,6 +942,13 @@ General Information)").c_str(),
938942
"Enables Language Server (LSP) mode. "
939943
"This won't compile input but serves as language server to to clients."
940944
)
945+
#if defined(SOLC_LSP_TCP)
946+
(
947+
"lsp-port",
948+
po::value<unsigned>()->value_name("PORT")->default_value(4040),
949+
"TCP listening port an LCP client can connect to instead of using stdio."
950+
)
951+
#endif
941952
(
942953
"lsp-trace",
943954
po::value<string>()->value_name("path"),
@@ -1129,9 +1140,13 @@ General Information)").c_str(),
11291140
// LSP related arguments.
11301141
"lsp",
11311142
"lsp-trace",
1143+
#if defined(SOLC_LSP_TCP)
1144+
"lsp-port",
1145+
#endif
11321146
// Defaulted arguments must be listed.
11331147
g_argModelCheckerEngine,
11341148
g_argModelCheckerTargets,
1149+
g_strModelCheckerContracts,
11351150
g_argOptimizeRuns
11361151
};
11371152

@@ -1809,6 +1824,19 @@ bool CommandLineInterface::serveLSP()
18091824
};
18101825

18111826
std::unique_ptr<lsp::Transport> transport = make_unique<lsp::JSONTransport>(traceLevel, traceLogger);
1827+
#if defined(SOLC_LSP_TCP)
1828+
if (m_args.count("lsp-port"))
1829+
{
1830+
unsigned const port = m_args.at("lsp-port").as<unsigned>();
1831+
if (port > 0xFFFF)
1832+
{
1833+
sout() << "LSP port number not in valid port range. " << endl;
1834+
return false;
1835+
}
1836+
transport = make_unique<lsp::LSPTCPTransport>(static_cast<unsigned short>(port), traceLevel, traceLogger);
1837+
}
1838+
#endif
1839+
18121840
lsp::LanguageServer languageServer(traceLogger, move(transport));
18131841
return languageServer.run();
18141842
}

solc/LSPTCPTransport.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#include <solc/LSPTCPTransport.h>
19+
20+
#include <optional>
21+
#include <string>
22+
#include <iostream>
23+
24+
namespace solidity::lsp {
25+
26+
using std::function;
27+
using std::nullopt;
28+
using std::optional;
29+
using std::string_view;
30+
using std::to_string;
31+
32+
using namespace std::string_literals;
33+
34+
LSPTCPTransport::LSPTCPTransport(unsigned short _port, Trace _traceLevel, function<void(string_view)> _trace):
35+
m_io_service(),
36+
m_endpoint(boost::asio::ip::make_address("127.0.0.1"), _port),
37+
m_acceptor(m_io_service),
38+
m_stream(),
39+
m_jsonTransport(),
40+
m_traceLevel{_traceLevel},
41+
m_trace(_trace ? std::move(_trace) : [](string_view) {})
42+
{
43+
m_acceptor.open(m_endpoint.protocol());
44+
m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
45+
m_acceptor.bind(m_endpoint);
46+
m_acceptor.listen();
47+
48+
if (m_traceLevel >= Trace::Messages)
49+
m_trace("Listening on tcp://127.0.0.1:"s + to_string(_port));
50+
}
51+
52+
void LSPTCPTransport::setTraceLevel(Trace _traceLevel)
53+
{
54+
m_traceLevel = _traceLevel;
55+
56+
if (m_jsonTransport)
57+
m_jsonTransport->setTraceLevel(_traceLevel);
58+
}
59+
60+
bool LSPTCPTransport::closed() const noexcept
61+
{
62+
return !m_acceptor.is_open();
63+
}
64+
65+
optional<Json::Value> LSPTCPTransport::receive()
66+
{
67+
auto const clientClosed = [&]() { return !m_stream || !m_stream.value().good() || m_stream.value().eof(); };
68+
69+
if (clientClosed())
70+
{
71+
m_stream.emplace(m_acceptor.accept());
72+
if (clientClosed())
73+
return nullopt;
74+
75+
auto const remoteAddr = m_stream.value().socket().remote_endpoint().address().to_string();
76+
auto const remotePort = m_stream.value().socket().remote_endpoint().port();
77+
m_trace("New client connected from "s + remoteAddr + ":" + to_string(remotePort) + ".");
78+
m_jsonTransport.emplace(m_stream.value(), m_stream.value(), m_traceLevel, [this](auto msg) { m_trace(msg); });
79+
}
80+
if (auto value = m_jsonTransport.value().receive(); value.has_value())
81+
return value;
82+
83+
if (clientClosed())
84+
{
85+
m_trace("Client disconnected.");
86+
m_jsonTransport.reset();
87+
m_stream.reset();
88+
}
89+
return nullopt;
90+
}
91+
92+
void LSPTCPTransport::notify(std::string const& _method, Json::Value const& _params)
93+
{
94+
if (m_jsonTransport.has_value())
95+
m_jsonTransport.value().notify(_method, _params);
96+
}
97+
98+
void LSPTCPTransport::reply(MessageID _id, Json::Value const& _result)
99+
{
100+
if (m_jsonTransport.has_value())
101+
m_jsonTransport.value().reply(_id, _result);
102+
}
103+
104+
void LSPTCPTransport::error(MessageID _id, ErrorCode _code, std::string const& _message)
105+
{
106+
if (m_jsonTransport.has_value())
107+
m_jsonTransport.value().error(_id, _code, _message);
108+
}
109+
110+
}

solc/LSPTCPTransport.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#pragma once
19+
20+
#include <libsolidity/lsp/Transport.h>
21+
22+
#include <boost/asio.hpp>
23+
#include <optional>
24+
25+
namespace solidity::lsp {
26+
27+
class LSPTCPTransport: public lsp::Transport {
28+
public:
29+
LSPTCPTransport(unsigned short _port, Trace _traceLevel, std::function<void(std::string_view)>);
30+
31+
void setTraceLevel(Trace _traceLevel) override;
32+
bool closed() const noexcept override;
33+
std::optional<Json::Value> receive() override;
34+
void notify(std::string const& _method, Json::Value const& _params) override;
35+
void reply(lsp::MessageID _id, Json::Value const& _result) override;
36+
void error(lsp::MessageID _id, lsp::ErrorCode _code, std::string const& _message) override;
37+
38+
private:
39+
boost::asio::io_service m_io_service;
40+
boost::asio::ip::tcp::endpoint m_endpoint;
41+
boost::asio::ip::tcp::acceptor m_acceptor;
42+
std::optional<boost::asio::ip::tcp::iostream> m_stream;
43+
std::optional<lsp::JSONTransport> m_jsonTransport;
44+
Trace m_traceLevel;
45+
std::function<void(std::string_view)> m_trace;
46+
};
47+
48+
} // end namespace

0 commit comments

Comments
 (0)