[Lldb-commits] [lldb] [lldb][rpc] Upstream RPC Client Library Emitters (PR #147655)
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 17 03:32:30 PDT 2025
================
@@ -0,0 +1,542 @@
+#include "RPCLibrarySourceEmitter.h"
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace clang;
+using namespace lldb_rpc_gen;
+
+static constexpr llvm::StringRef ReturnVariableName("__result");
+
+// This map stores any method that needs custom logic with a struct that
+// tells us where the logic needs to be inserted and what code needs to be
+// inserted. The code here is stored as a raw string literal.
+const llvm::StringMap<RPCLibrarySourceEmitter::CustomLogic>
+ CustomLogicForMethods = {
+ {"_ZN4lldb10SBDebugger6CreateEbPFvPKcPvES3_",
+ {RPCLibrarySourceEmitter::CustomLogicLocation::eAfterDecode, R"code(
+ // Now source the .lldbinit files manually since we can't rely on the
+ // LLDB.framework on the other side to have special support for sourcing the right file
+ // since it would try to source "~/.lldbinit-lldb-rpc-server" followed by
+ // "~/.lldbinit". We want it to try "~.lldbinit-%s" where %s is the
+ // current program basename followed by "~/.lldbinit".
+
+ if (source_init_files && __result.ObjectRefIsValid()) {
+ const char *program_basename = rpc::GetProgramBasename();
+ if (program_basename) {
+ char init_path[PATH_MAX];
+ snprintf(init_path, sizeof(init_path), "~/.lldbinit-%s",
+ program_basename);
+ lldb_rpc::SBFileSpec program_init_file(connection, init_path, true);
+ if (program_init_file.Exists()) {
+ char command_str[PATH_MAX];
+ snprintf(command_str, sizeof(command_str),
+ "command source -s 1 -c 1 -e 0 '%s'", init_path);
+ __result.HandleCommand(command_str);
+ } else {
+ __result.HandleCommand("command source -s 1 -c 1 -e 0 '~/.lldbinit'");
+ }
+ }
+ })code"}},
+};
+
+static std::string GetLocalObjectRefCtor(const std::string &ClassName) {
+ return "rpc::LocalObjectRef(LLDB_RPC_INVALID_CONNECTION_ID, eClass_lldb_" +
+ ClassName + ", LLDB_RPC_INVALID_OBJECT_ID)";
+}
+
+static std::string GetObjectRefCtor(const std::string &ClassName) {
+ return "rpc::ObjectRef(LLDB_RPC_INVALID_CONNECTION_ID, eClass_lldb_" +
+ ClassName + ", LLDB_RPC_INVALID_OBJECT_ID)";
+}
+
+void RPCLibrarySourceEmitter::EmitCopyCtor() {
+ EmitLine("lldb_rpc::" + CurrentClass + "::" + CurrentClass +
+ "(const lldb_rpc::" + CurrentClass + " &rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitCopyAssign() {
+ EmitLine("lldb_rpc::" + CurrentClass + " &lldb_rpc::" + CurrentClass +
+ "::operator=(const lldb_rpc::" + CurrentClass + " &rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMoveCtor() {
+ EmitLine("lldb_rpc::" + CurrentClass + "::" + CurrentClass +
+ "(lldb_rpc::" + CurrentClass + " &&rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMoveAssign() {
+ EmitLine("lldb_rpc::" + CurrentClass + " &lldb_rpc::" + CurrentClass +
+ "::operator=(lldb_rpc::" + CurrentClass + " &&rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMethod(const Method &method) {
+ if (method.IsCopyCtor) {
+ CopyCtorEmitted = true;
+ EmitCopyCtor();
+ return;
+ } else if (method.IsCopyAssign) {
+ CopyAssignEmitted = true;
+ EmitCopyAssign();
+ return;
+ } else if (method.IsMoveCtor) {
+ MoveCtorEmitted = true;
+ EmitMoveCtor();
+ return;
+ } else if (method.IsMoveAssign) {
+ MoveAssignEmitted = true;
+ EmitMoveAssign();
+ return;
+ }
+
+ EmitCommentHeader(method);
+ EmitFunctionHeader(method);
+ EmitFunctionBody(method);
+ EmitFunctionFooter();
+}
+
+void RPCLibrarySourceEmitter::StartClass(std::string ClassName) {
+ CurrentClass = std::move(ClassName);
+ if (lldb_rpc_gen::SBClassRequiresDefaultCtor(CurrentClass)) {
+ std::string BaseClassCtor =
+ lldb_rpc_gen::SBClassInheritsFromObjectRef(CurrentClass)
+ ? GetObjectRefCtor(CurrentClass)
+ : GetLocalObjectRefCtor(CurrentClass);
+ EmitLine("lldb_rpc::" + CurrentClass + "::" + CurrentClass +
+ "() : " + BaseClassCtor + " {}");
+ }
+}
+
+void RPCLibrarySourceEmitter::EndClass() {
+ if (lldb_rpc_gen::SBClassRequiresCopyCtorAssign(CurrentClass)) {
+ if (!CopyCtorEmitted)
+ EmitCopyCtor();
+
+ if (!CopyAssignEmitted)
+ EmitCopyAssign();
+ }
+
+ if (!MoveCtorEmitted)
+ EmitMoveCtor();
+
+ if (!MoveAssignEmitted)
+ EmitMoveAssign();
+
+ CopyCtorEmitted = false;
+ CopyAssignEmitted = false;
+ MoveCtorEmitted = false;
+ MoveAssignEmitted = false;
+}
+
+void RPCLibrarySourceEmitter::EmitCommentHeader(const Method &method) {
+ std::string CommentLine;
+ llvm::raw_string_ostream CommentStream(CommentLine);
+
+ CommentStream << "// "
+ << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ method.QualifiedName)
+ << "(" << method.CreateParamListAsString(eLibrary) << ")";
+ if (method.IsConst)
+ CommentStream << " const";
+
+ EmitLine("//-----------------------------------------------------------");
+ EmitLine(CommentLine);
+ EmitLine("//-----------------------------------------------------------");
+}
+
+void RPCLibrarySourceEmitter::EmitFunctionHeader(const Method &method) {
+ std::string FunctionHeader;
+ llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader);
+
+ if (!method.IsDtor && !method.IsConversionMethod && !method.IsCtor)
+ FunctionHeaderStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ method.ReturnType.getAsString(method.Policy))
+ << " ";
+
+ FunctionHeaderStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ method.QualifiedName)
+ << "(" << method.CreateParamListAsString(eLibrary)
+ << ")";
+ if (method.IsConst)
+ FunctionHeaderStream << " const";
+ if (method.IsCtor) {
+ if (lldb_rpc_gen::SBClassInheritsFromObjectRef(method.BaseName))
+ FunctionHeaderStream << " : " << GetObjectRefCtor(method.BaseName);
+ else
+ FunctionHeaderStream << " : " << GetLocalObjectRefCtor(method.BaseName);
+ }
+ FunctionHeaderStream << " {";
+
+ EmitLine(FunctionHeader);
+ IndentLevel++;
+}
+
+void RPCLibrarySourceEmitter::EmitFunctionBody(const Method &method) {
+ // There's nothing to do for destructors. The LocalObjectRef destructor should
+ // handle everything for us.
+ if (method.IsDtor)
+ return;
+
+ EmitLine("// 1) Perform setup");
+ EmitFunctionSetup(method);
+ EmitLine("// 2) Send RPC call");
+ EmitSendRPCCall(method);
+ EmitLine("// 3) Decode return values");
+ EmitDecodeReturnValues(method);
+}
+
+void RPCLibrarySourceEmitter::EmitFunctionFooter() {
+ IndentLevel--;
+ EmitLine("}");
+}
+
+void RPCLibrarySourceEmitter::EmitFunctionSetup(const Method &method) {
+ if (!method.ReturnType->isVoidType())
+ EmitReturnValueStorage(method);
+
+ EmitConnectionSetup(method);
+
+ EmitLine("// RPC Communication setup");
+ EmitLine("static RPCFunctionInfo g_func(\"" + method.MangledName + "\");");
+ EmitLine("RPCStream send;");
+ EmitLine("RPCStream response;");
+ EmitLine("g_func.Encode(send);");
+
+ EmitEncodeParameters(method);
+
+ if (CustomLogicForMethods.lookup(method.MangledName).Location ==
+ CustomLogicLocation::eAfterSetup)
+ EmitCustomLogic(method);
+}
+
+void RPCLibrarySourceEmitter::EmitReturnValueStorage(const Method &method) {
+ assert(!method.ReturnType->isVoidType() &&
+ "Cannot emit return value storage when return type is 'void'");
+
+ EmitLine("// Storage for return value");
+ std::string ReturnValueStorage;
+ llvm::raw_string_ostream ReturnValueStorageStream(ReturnValueStorage);
+
+ std::string ReturnValueType;
+ if (lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType))
+ ReturnValueStorageStream << "rpc_common::ConstCharPointer "
+ << ReturnVariableName << ";";
+ else if (method.ReturnType->isPointerType())
+ ReturnValueStorageStream << "Bytes " << ReturnVariableName << ";";
+ else {
+ // We need to get the unqualified type because we don't want the return
+ // variable to be marked `const`. That would prevent us from changing it
+ // during the decoding step.
+ QualType UnqualifiedReturnType = method.ReturnType.getUnqualifiedType();
+ ReturnValueStorageStream
+ << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ UnqualifiedReturnType.getAsString(method.Policy))
+ << " " << ReturnVariableName << " = {};";
+ }
+ EmitLine(ReturnValueStorage);
+}
+
+void RPCLibrarySourceEmitter::EmitConnectionSetup(const Method &method) {
+ // Methods know if they require a connection parameter. We need to figure out
+ // which scenario we're in.
+ bool ConnectionDerived = false;
+ if (!method.RequiresConnectionParameter()) {
+ // If we have an instance method that is not a constructor, we have a valid
+ // connection from `this` via `ObjectRefGetConnectionSP()`.
+ if (!method.IsCtor && method.IsInstance) {
+ EmitLine("// Deriving connection from this.");
+ EmitLine("rpc_common::ConnectionSP connection_sp = "
+ "ObjectRefGetConnectionSP();");
+ ConnectionDerived = true;
+ }
+
+ // Otherewise, we try to derive it from an existing parameter.
+ if (!ConnectionDerived)
+ for (const auto &Param : method.Params) {
+ if (lldb_rpc_gen::TypeIsSBClass(Param.Type)) {
+ EmitLine("// Deriving connection from SB class parameter.");
+ std::string ConnectionLine =
+ "rpc_common::ConnectionSP connection_sp = " + Param.Name;
+ if (Param.Type->isPointerType())
+ ConnectionLine += "->";
+ else
+ ConnectionLine += ".";
+ ConnectionLine += "ObjectRefGetConnectionSP();";
+ EmitLine(ConnectionLine);
+ ConnectionDerived = true;
+ break;
+ }
+ }
+ } else {
+ // This method requires a connection parameter. It will always be named
+ // "connection" and it will always come first in the parameter list.
+ EmitLine("// Using connection parameter.");
+ EmitLine(
+ "rpc_common::ConnectionSP connection_sp = connection.GetConnection();");
+ ConnectionDerived = true;
+ }
+
+ assert(ConnectionDerived &&
+ "Unable to determine where method should derive connection from");
+
+ // NOTE: By this point, we should have already emitted the storage for the
+ // return value.
+ std::string FailureReturnExpression;
+ if (method.ReturnType->isPointerType())
+ FailureReturnExpression = "nullptr";
+ else if (!method.ReturnType->isVoidType())
+ FailureReturnExpression = "__result";
+ EmitLine("if (!connection_sp) return " + FailureReturnExpression + ";");
+}
+
+void RPCLibrarySourceEmitter::EmitEncodeParameters(const Method &method) {
+ // Encode parameters.
+ if (method.IsInstance && !method.IsCtor)
+ EmitLine("RPCValueEncoder(send, "
+ "rpc_common::RPCPacket::ValueType::Argument, *this);");
+
+ for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) {
----------------
DavidSpickett wrote:
If Iter is not modified within the loop, use a range based for if you can make it work.
https://github.com/llvm/llvm-project/pull/147655
More information about the lldb-commits
mailing list