[llvm] [orc-rt] Add ConnectionSpec, a connection-string parser (PR #225304)
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 22:56:32 PDT 2026
https://github.com/lhames created https://github.com/llvm/llvm-project/pull/225304
Parses the string a process is given to describe the connection it should establish with its peer:
<transport>[:<action>]=<descriptor>
Checking is punctuation only: the transport and action are opaque tokens and the descriptor's syntax belongs to the transport, so a spec parses without knowing which transports this process was built with.
The grammar is LLVM's (#225075), so the same vocabulary reaches an orc-rt executor and an LLVM controller. A registry mapping transports to the connectors that implement them will be added in a follow-up commit.
>From d3dc1d185c711d24629aee749099cb5e40269bb8 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Tue, 22 Sep 2026 14:06:08 +1000
Subject: [PATCH] [orc-rt] Add ConnectionSpec, a connection-string parser
Parses the string a process is given to describe the connection it
should establish with its peer:
<transport>[:<action>]=<descriptor>
Checking is punctuation only: the transport and action are opaque tokens
and the descriptor's syntax belongs to the transport, so a spec parses
without knowing which transports this process was built with.
The grammar is LLVM's (#225075), so the same vocabulary reaches an
orc-rt executor and an LLVM controller. A registry mapping transports to
the connectors that implement them will be added in a follow-up commit.
---
.../include/orc-rt/bedrock/ConnectionSpec.h | 96 +++++++++++++++++++
orc-rt/lib/bedrock/CMakeLists.txt | 1 +
orc-rt/lib/bedrock/ConnectionSpec.cpp | 50 ++++++++++
orc-rt/test/unit/CMakeLists.txt | 1 +
.../test/unit/bedrock/ConnectionSpecTest.cpp | 83 ++++++++++++++++
5 files changed, 231 insertions(+)
create mode 100644 orc-rt/include/orc-rt/bedrock/ConnectionSpec.h
create mode 100644 orc-rt/lib/bedrock/ConnectionSpec.cpp
create mode 100644 orc-rt/test/unit/bedrock/ConnectionSpecTest.cpp
diff --git a/orc-rt/include/orc-rt/bedrock/ConnectionSpec.h b/orc-rt/include/orc-rt/bedrock/ConnectionSpec.h
new file mode 100644
index 0000000000000..a5351ea18d63c
--- /dev/null
+++ b/orc-rt/include/orc-rt/bedrock/ConnectionSpec.h
@@ -0,0 +1,96 @@
+//===- ConnectionSpec.h - Connection spec parsing ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A ConnectionSpec describes one connection a process should establish with
+// its peer, e.g. "tcp:connect=localhost:20000" or "socket:adopt=3".
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_BEDROCK_CONNECTIONSPEC_H
+#define ORC_RT_BEDROCK_CONNECTIONSPEC_H
+
+#include "orc-rt/support/Error.h"
+
+#include <string>
+#include <string_view>
+
+namespace orc_rt {
+
+/// The parsed form of a string describing one connection a process should
+/// establish with its peer:
+///
+/// <transport>[:<action>]=<descriptor>
+///
+/// transport what kind of thing the descriptor names: "tcp" for a host:port
+/// endpoint, "socket" for a handle this process already holds.
+/// action what this process does with it: "connect", "listen", or
+/// "adopt" for a handle it was handed.
+/// descriptor the thing itself, in whatever syntax the transport defines.
+///
+/// E.g. "tcp:connect=localhost:20000", "tcp:listen=[::1]:0", "socket:adopt=3".
+///
+/// A spec says what the process reading it does, so the two ends of one
+/// connection carry different specs: an executor told
+/// "tcp:connect=<host>:<port>" pairs with a controller told "tcp:listen=:0".
+///
+/// Parsing checks punctuation only: transport and action are opaque tokens, the
+/// descriptor's syntax belongs to the transport, and all three fields are kept
+/// verbatim -- so a caller matching a name against a known set does so
+/// case-sensitively. Only the text before the first '=' is searched for the
+/// ':', which lets a descriptor hold ':' and '=' unescaped.
+class ConnectionSpec {
+public:
+ /// Parses Spec as <transport>[:<action>]=<descriptor>.
+ ///
+ /// Checks only the punctuation: that there is an '=', and that the names
+ /// either side of it are non-empty where required.
+ static Expected<ConnectionSpec> parse(std::string_view Spec) noexcept;
+
+ ConnectionSpec() = default;
+
+ /// Return true if this is an empty spec.
+ bool empty() const noexcept {
+ return Transport.empty() && Action.empty() && Descriptor.empty();
+ }
+
+ /// The transport name, e.g. "tcp". Never empty.
+ const std::string &transport() const noexcept { return Transport; }
+
+ /// The action name, e.g. "connect" or "adopt". May be empty.
+ const std::string &action() const noexcept { return Action; }
+
+ /// The thing the transport names, in whatever syntax that transport defines.
+ /// Opaque here: the parser neither splits nor validates it. May be empty.
+ const std::string &descriptor() const noexcept { return Descriptor; }
+
+ /// Rebuilds the original connection string, e.g. "tcp:connect=host:port".
+ std::string str() const noexcept {
+ std::string S = Transport;
+ if (!Action.empty()) {
+ S += ':';
+ S += Action;
+ }
+ S += '=';
+ S += Descriptor;
+ return S;
+ }
+
+private:
+ ConnectionSpec(std::string Transport, std::string Action,
+ std::string Descriptor) noexcept
+ : Transport(std::move(Transport)), Action(std::move(Action)),
+ Descriptor(std::move(Descriptor)) {}
+
+ std::string Transport;
+ std::string Action;
+ std::string Descriptor;
+};
+
+} // namespace orc_rt
+
+#endif // ORC_RT_BEDROCK_CONNECTIONSPEC_H
diff --git a/orc-rt/lib/bedrock/CMakeLists.txt b/orc-rt/lib/bedrock/CMakeLists.txt
index 5dc584bca9659..99016c5866d41 100644
--- a/orc-rt/lib/bedrock/CMakeLists.txt
+++ b/orc-rt/lib/bedrock/CMakeLists.txt
@@ -1,6 +1,7 @@
set(ORC_RT_BEDROCK_SOURCES
BootstrapInfo.cpp
+ ConnectionSpec.cpp
ExecutorProcessInfo.cpp
GDBJITRegistrar.cpp
InProcessControllerAccess.cpp
diff --git a/orc-rt/lib/bedrock/ConnectionSpec.cpp b/orc-rt/lib/bedrock/ConnectionSpec.cpp
new file mode 100644
index 0000000000000..0b517e62b6c8b
--- /dev/null
+++ b/orc-rt/lib/bedrock/ConnectionSpec.cpp
@@ -0,0 +1,50 @@
+//===- ConnectionSpec.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Connection spec parsing.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/bedrock/ConnectionSpec.h"
+
+#include "orc-rt-internal/support/StringExtras.h"
+
+namespace orc_rt {
+
+Expected<ConnectionSpec> ConnectionSpec::parse(std::string_view Spec) noexcept {
+ auto Fail = [&](std::string_view Msg) -> Error {
+ StringOutputStream OS;
+ OS << "in connection spec '" << Spec << "': " << Msg;
+ return make_error<StringError>(std::move(OS).str());
+ };
+
+ auto EqPos = Spec.find('=');
+ if (EqPos == std::string_view::npos)
+ return Fail("expected '<transport>[:<action>]=<descriptor>', but found "
+ "no '='");
+
+ auto Descriptor = Spec.substr(EqPos + 1);
+ auto LHS = Spec.substr(0, EqPos);
+ std::string_view Transport, Action;
+
+ if (auto ColonPos = LHS.find(':'); ColonPos != std::string_view::npos) {
+ Transport = LHS.substr(0, ColonPos);
+ Action = LHS.substr(ColonPos + 1);
+ if (Action.empty())
+ return Fail("empty action name");
+ } else
+ Transport = LHS;
+
+ if (Transport.empty())
+ return Fail("empty transport name");
+
+ return ConnectionSpec(std::string(Transport), std::string(Action),
+ std::string(Descriptor));
+}
+
+} // namespace orc_rt
diff --git a/orc-rt/test/unit/CMakeLists.txt b/orc-rt/test/unit/CMakeLists.txt
index 41132cc786d10..208be6313036d 100644
--- a/orc-rt/test/unit/CMakeLists.txt
+++ b/orc-rt/test/unit/CMakeLists.txt
@@ -107,6 +107,7 @@ endif()
add_orc_rt_unittest(BedrockTests
bedrock/BootstrapInfoTest.cpp
+ bedrock/ConnectionSpecTest.cpp
bedrock/ExecutorProcessInfoTest.cpp
bedrock/InProcessControllerAccessTest.cpp
bedrock/NativeDylibManagerTest.cpp
diff --git a/orc-rt/test/unit/bedrock/ConnectionSpecTest.cpp b/orc-rt/test/unit/bedrock/ConnectionSpecTest.cpp
new file mode 100644
index 0000000000000..7bea53ee7712e
--- /dev/null
+++ b/orc-rt/test/unit/bedrock/ConnectionSpecTest.cpp
@@ -0,0 +1,83 @@
+//===- ConnectionSpecTest.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/bedrock/ConnectionSpec.h"
+#include "orc-rt/support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace orc_rt;
+
+namespace {
+
+TEST(ConnectionSpecTest, TransportActionAndDescriptor) {
+ auto CS = ConnectionSpec::parse("tcp:connect=localhost:20000");
+ ASSERT_TRUE(!!CS);
+ EXPECT_EQ(CS->transport(), "tcp");
+ EXPECT_EQ(CS->action(), "connect");
+ EXPECT_EQ(CS->descriptor(), "localhost:20000");
+}
+
+TEST(ConnectionSpecTest, TransportActionAndMultiPartDescriptor) {
+ auto CS = ConnectionSpec::parse("pipe:adopt=3,4");
+ ASSERT_TRUE(!!CS);
+ EXPECT_EQ(CS->transport(), "pipe");
+ EXPECT_EQ(CS->action(), "adopt");
+ EXPECT_EQ(CS->descriptor(), "3,4");
+}
+
+TEST(ConnectionSpecTest, EmptyDescriptorIsAllowed) {
+ // Whether an empty descriptor means anything is the transport's business, so
+ // it isn't a syntax error here.
+ auto CS = ConnectionSpec::parse("socket:adopt=");
+ ASSERT_TRUE(!!CS);
+ EXPECT_EQ(CS->transport(), "socket");
+ EXPECT_EQ(CS->descriptor(), "");
+}
+
+TEST(ConnectionSpecTest, DescriptorKeepsColonsAndEquals) {
+ auto CS = ConnectionSpec::parse("tcp:listen=[::1]:0");
+ ASSERT_TRUE(!!CS);
+ EXPECT_EQ(CS->transport(), "tcp");
+ EXPECT_EQ(CS->action(), "listen");
+ EXPECT_EQ(CS->descriptor(), "[::1]:0");
+
+ // Only the first '=' is structural.
+ auto Odd = ConnectionSpec::parse("unix:listen=/tmp/a=b.sock");
+ ASSERT_TRUE(!!Odd);
+ EXPECT_EQ(Odd->descriptor(), "/tmp/a=b.sock");
+}
+
+TEST(ConnectionSpecTest, MissingEqualsFails) {
+ auto CS = ConnectionSpec::parse("tcp:connect");
+ EXPECT_FALSE(!!CS);
+ consumeError(CS.takeError());
+}
+
+TEST(ConnectionSpecTest, EmptyTransportFails) {
+ auto CS = ConnectionSpec::parse("=localhost:20000");
+ EXPECT_FALSE(!!CS);
+ consumeError(CS.takeError());
+
+ auto WithAction = ConnectionSpec::parse(":connect=localhost:20000");
+ EXPECT_FALSE(!!WithAction);
+ consumeError(WithAction.takeError());
+}
+
+TEST(ConnectionSpecTest, EmptyActionFails) {
+ auto CS = ConnectionSpec::parse("tcp:=localhost:20000");
+ EXPECT_FALSE(!!CS);
+ consumeError(CS.takeError());
+}
+
+TEST(ConnectionSpecTest, ErrorMessageQuotesTheSpec) {
+ auto CS = ConnectionSpec::parse("tcp:connect");
+ ASSERT_FALSE(!!CS);
+ EXPECT_NE(toString(CS.takeError()).find("'tcp:connect'"), std::string::npos);
+}
+
+} // namespace
More information about the llvm-commits
mailing list