https://github.com/chelcassanova updated 
https://github.com/llvm/llvm-project/pull/138032

>From b6edf90f106ee2b339a162e13058167899f2ee21 Mon Sep 17 00:00:00 2001
From: Chelsea Cassanova <chelsea_cassan...@apple.com>
Date: Wed, 30 Apr 2025 14:24:03 -0700
Subject: [PATCH] [lldb[RPC] Upstream RPC server interface emitters

This commit upstreams the LLDB RPC server interface emitters. These
emitters generate the server-side API interfaces for RPC, which
communicate directly with liblldb itself out of process using the SB
API.

https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804
---
 .../Inputs/Server/CheckBasicIncludesEmit.h    |   6 +
 .../Inputs/Server/CheckConstCharPointer.h     |  14 +
 .../Tests/Server/CheckBasicIncludesEmit.test  |  14 +
 .../Tests/Server/CheckConstCharPointer.test   |  10 +
 .../server/RPCServerHeaderEmitter.cpp         |  75 +++
 .../server/RPCServerHeaderEmitter.h           |  47 ++
 .../server/RPCServerSourceEmitter.cpp         | 584 ++++++++++++++++++
 .../server/RPCServerSourceEmitter.h           |  81 +++
 8 files changed, 831 insertions(+)
 create mode 100644 
lldb/test/Shell/RPC/Generator/Inputs/Server/CheckBasicIncludesEmit.h
 create mode 100644 
lldb/test/Shell/RPC/Generator/Inputs/Server/CheckConstCharPointer.h
 create mode 100644 
lldb/test/Shell/RPC/Generator/Tests/Server/CheckBasicIncludesEmit.test
 create mode 100644 
lldb/test/Shell/RPC/Generator/Tests/Server/CheckConstCharPointer.test
 create mode 100644 
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
 create mode 100644 
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
 create mode 100644 
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
 create mode 100644 
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h

diff --git 
a/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckBasicIncludesEmit.h 
b/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckBasicIncludesEmit.h
new file mode 100644
index 0000000000000..77394aba12f7a
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckBasicIncludesEmit.h
@@ -0,0 +1,6 @@
+// This ia a basic header file used to check that the server-side emitter
+// for rpc-gen emits an expected set of includes in a generated source file.
+#ifndef LLDB_API_SBRPC_CHECKBASICINCLUDE_H
+#define LLDB_API_SBRPC_CHECKBASICINCLUDE_H
+
+#endif // LLDB_API_SBRPC_CHECKBASICINCLUDE_H
diff --git 
a/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckConstCharPointer.h 
b/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckConstCharPointer.h
new file mode 100644
index 0000000000000..37121cd445267
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Server/CheckConstCharPointer.h
@@ -0,0 +1,14 @@
+#ifndef LLDB_API_SBRPC_CHECKCONSTCHARPOINTER_H
+#define LLDB_API_SBRPC_CHECKCONSTCHARPOINTER_H
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKCONSTCHARPOINTER {
+public:
+  // const char * parameters must decoded as rpc_common::ConstCharPointer in 
server side
+  // source files.
+  int CheckConstCharPointer(char *string);
+
+}; // class SBRPC_CHECKCONSTCHARPOINTER
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKCONSTCHARPOINTER_H
diff --git 
a/lldb/test/Shell/RPC/Generator/Tests/Server/CheckBasicIncludesEmit.test 
b/lldb/test/Shell/RPC/Generator/Tests/Server/CheckBasicIncludesEmit.test
new file mode 100644
index 0000000000000..535d31886df6e
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Server/CheckBasicIncludesEmit.test
@@ -0,0 +1,14 @@
+UNSUPPORTED: target=*
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckBasicIncludesEmit.h
+
+RUN: cat %t/lib/CheckBasicIncludesEmit.cpp | FileCheck %s
+
+# All server-side source files must have these includes at the top of their 
files.
+CHECK: #include "RPCUserServer.h"
+CHECK: #include "SBAPI.h"
+CHECK: #include <lldb-rpc/common/RPCArgument.h>
+CHECK: #include <lldb-rpc/common/RPCCommon.h>
+CHECK: #include <lldb-rpc/common/RPCFunction.h>
+CHECK: #include <lldb/API/LLDB.h>
diff --git 
a/lldb/test/Shell/RPC/Generator/Tests/Server/CheckConstCharPointer.test 
b/lldb/test/Shell/RPC/Generator/Tests/Server/CheckConstCharPointer.test
new file mode 100644
index 0000000000000..e3d92fa583431
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Server/CheckConstCharPointer.test
@@ -0,0 +1,10 @@
+UNSUPPORTED: target=*
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckConstCharPointer.h
+
+RUN: cat %t/lib/CheckConstCharPointer.cpp | FileCheck %s
+
+# const char * pointers must be decoded as rpc_common::ConstCharPointer objects
+# in server side source files.
+CHECK: rpc_common::ConstCharPointer string
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
new file mode 100644
index 0000000000000..30a18e3c7044b
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
@@ -0,0 +1,75 @@
+//===-- RPCServerHeaderEmitter.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 "RPCServerHeaderEmitter.h"
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace lldb_rpc_gen;
+
+void RPCServerHeaderEmitter::EmitMethod(const Method &method) {
+  // We'll be using the mangled name in order to disambiguate
+  // overloaded methods.
+  const std::string &MangledName = method.MangledName;
+
+  EmitLine("class " + MangledName +
+           " : public rpc_common::RPCFunctionInstance {");
+  EmitLine("public:");
+  IndentLevel++;
+  EmitConstructor(MangledName);
+  EmitDestructor(MangledName);
+  EmitHandleRPCCall();
+  IndentLevel--;
+  EmitLine("};");
+}
+
+void RPCServerHeaderEmitter::EmitHandleRPCCall() {
+  EmitLine("bool HandleRPCCall(rpc_common::Connection &connection, "
+           "rpc_common::RPCStream &send, rpc_common::RPCStream &response) "
+           "override;");
+}
+
+void RPCServerHeaderEmitter::EmitConstructor(const std::string &MangledName) {
+  EmitLine(MangledName + "() : RPCFunctionInstance(\"" + MangledName +
+           "\") {}");
+}
+
+void RPCServerHeaderEmitter::EmitDestructor(const std::string &MangledName) {
+  EmitLine("~" + MangledName + "() override {}");
+}
+
+std::string RPCServerHeaderEmitter::GetHeaderGuard() {
+  const std::string UpperFilenameNoExt =
+      llvm::sys::path::stem(
+          llvm::sys::path::filename(OutputFile->getFilename()))
+          .upper();
+  return "GENERATED_LLDB_RPC_SERVER_" + UpperFilenameNoExt + "_H";
+}
+
+void RPCServerHeaderEmitter::Begin() {
+  const std::string HeaderGuard = GetHeaderGuard();
+  EmitLine("#ifndef " + HeaderGuard);
+  EmitLine("#define " + HeaderGuard);
+  EmitLine("");
+  EmitLine("#include <lldb-rpc/common/RPCFunction.h>");
+  EmitLine("");
+  EmitLine("namespace rpc_server {");
+}
+
+void RPCServerHeaderEmitter::End() {
+  EmitLine("} // namespace rpc_server");
+  EmitLine("#endif // " + GetHeaderGuard());
+}
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
new file mode 100644
index 0000000000000..a0e4f47d4f570
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
@@ -0,0 +1,47 @@
+//===-- RPCServerHeaderEmitter.h ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_RPC_GEN_RPCSERVERHEADEREMITTER_H
+#define LLDB_RPC_GEN_RPCSERVERHEADEREMITTER_H
+
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+using namespace clang;
+
+namespace lldb_rpc_gen {
+/// Emit the source code for server-side *.h files.
+class RPCServerHeaderEmitter : public FileEmitter {
+public:
+  RPCServerHeaderEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile)
+      : FileEmitter(std::move(OutputFile)) {
+    Begin();
+  }
+
+  ~RPCServerHeaderEmitter() { End(); }
+
+  void EmitMethod(const Method &method);
+
+private:
+  void EmitHandleRPCCall();
+
+  void EmitConstructor(const std::string &MangledName);
+
+  void EmitDestructor(const std::string &MangledName);
+
+  std::string GetHeaderGuard();
+
+  void Begin();
+
+  void End();
+};
+} // namespace lldb_rpc_gen
+
+#endif // LLDB_RPC_GEN_RPCSERVERHEADEREMITTER_H
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
new file mode 100644
index 0000000000000..a6d2528fe2c3c
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
@@ -0,0 +1,584 @@
+//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h"
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+
+using namespace clang;
+using namespace lldb_rpc_gen;
+
+// For methods with pointer return types, it's important that we know how big
+// the type of the pointee is. We must correctly size a buffer (in the form of 
a
+// Bytes object) before we can actually use it.
+static const std::map<llvm::StringRef, size_t> MethodsWithPointerReturnTypes = 
{
+    {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16
+    {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16},     // sizeof(uuid_t) -> 16
+};
+
+void RPCServerSourceEmitter::EmitMethod(const Method &method) {
+  if (method.ContainsFunctionPointerParameter)
+    EmitCallbackFunction(method);
+
+  EmitCommentHeader(method);
+  EmitFunctionHeader(method);
+  EmitFunctionBody(method);
+  EmitFunctionFooter();
+}
+
+void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) {
+  std::string CommentLine;
+  llvm::raw_string_ostream CommentStream(CommentLine);
+
+  CommentStream << "// " << method.QualifiedName << "("
+                << method.CreateParamListAsString(eServer) << ")";
+  if (method.IsConst)
+    CommentStream << " const";
+
+  EmitLine("//------------------------------------------------------------");
+  EmitLine(CommentLine);
+  EmitLine("//------------------------------------------------------------");
+}
+
+void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) {
+  std::string FunctionHeader;
+  llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader);
+  FunctionHeaderStream
+      << "bool rpc_server::" << method.MangledName
+      << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream "
+         "&send, RPCStream &response) {";
+  EmitLine(FunctionHeader);
+  IndentLevel++;
+}
+
+void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) {
+  EmitLine("// 1) Make local storage for incoming function arguments");
+  EmitStorageForParameters(method);
+  EmitLine("// 2) Decode all function arguments");
+  EmitDecodeForParameters(method);
+  EmitLine("// 3) Call the method and encode the return value");
+  EmitMethodCallAndEncode(method);
+}
+
+void RPCServerSourceEmitter::EmitFunctionFooter() {
+  EmitLine("return true;");
+  IndentLevel--;
+  EmitLine("}");
+}
+
+void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) {
+  // If we have an instance method and it isn't a constructor, we'll need to
+  // emit a "this" pointer.
+  if (method.IsInstance && !method.IsCtor)
+    EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy,
+                               /* IsFollowedByLen = */ false);
+  for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) 
{
+    EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy,
+                               Iter->IsFollowedByLen);
+    // Skip over the length parameter, we don't emit it.
+    if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
+        Iter->IsFollowedByLen)
+      Iter++;
+  }
+}
+
+void RPCServerSourceEmitter::EmitStorageForOneParameter(
+    QualType ParamType, const std::string &ParamName,
+    const PrintingPolicy &Policy, bool IsFollowedByLen) {
+  // First, we consider `const char *`, `const char **`. They have special
+  // server-side types.
+  if (TypeIsConstCharPtr(ParamType)) {
+    EmitLine("rpc_common::ConstCharPointer " + ParamName + ";");
+    return;
+  } else if (TypeIsConstCharPtrPtr(ParamType)) {
+    EmitLine("rpc_common::StringList " + ParamName + ";");
+    return;
+  }
+
+  QualType UnderlyingType =
+      lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType);
+  const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType);
+
+  if (ParamType->isPointerType() && !IsSBClass) {
+    // Void pointer with no length is usually a baton for a callback. We're
+    // going to hold onto the pointer value so we can send it back to the
+    // client-side when we implement callbacks.
+    if (ParamType->isVoidPointerType() && !IsFollowedByLen) {
+      EmitLine("void * " + ParamName + " = nullptr;");
+      return;
+    }
+
+    if (!ParamType->isFunctionPointerType()) {
+      EmitLine("Bytes " + ParamName + ";");
+      return;
+    }
+
+    assert(ParamType->isFunctionPointerType() && "Unhandled pointer type");
+    EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;");
+    return;
+  }
+
+  std::string StorageDeclaration;
+  llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration);
+
+  UnderlyingType.print(StorageDeclarationStream, Policy);
+  StorageDeclarationStream << " ";
+  if (IsSBClass)
+    StorageDeclarationStream << "*";
+  StorageDeclarationStream << ParamName;
+  if (IsSBClass)
+    StorageDeclarationStream << " = nullptr";
+  else
+    StorageDeclarationStream << " = {}";
+  StorageDeclarationStream << ";";
+  EmitLine(StorageDeclaration);
+}
+
+void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) {
+  if (method.IsInstance && !method.IsCtor)
+    EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy);
+  for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) 
{
+    EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy);
+    if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
+        Iter->IsFollowedByLen)
+      Iter++;
+  }
+}
+
+void RPCServerSourceEmitter::EmitDecodeForOneParameter(
+    QualType ParamType, const std::string &ParamName,
+    const PrintingPolicy &Policy) {
+  QualType UnderlyingType =
+      lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType);
+
+  if (TypeIsSBClass(UnderlyingType)) {
+    std::string DecodeLine;
+    llvm::raw_string_ostream DecodeLineStream(DecodeLine);
+    DecodeLineStream << ParamName << " = "
+                     << "RPCServerObjectDecoder<";
+    UnderlyingType.print(DecodeLineStream, Policy);
+    DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);";
+    EmitLine(DecodeLine);
+    EmitLine("if (!" + ParamName + ")");
+    IndentLevel++;
+    EmitLine("return false;");
+    IndentLevel--;
+  } else {
+    EmitLine("if (!RPCValueDecoder(send, "
+             "rpc_common::RPCPacket::ValueType::Argument, " +
+             ParamName + "))");
+    IndentLevel++;
+    EmitLine("return false;");
+    IndentLevel--;
+  }
+}
+
+std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) {
+  std::string MethodCall;
+  llvm::raw_string_ostream MethodCallStream(MethodCall);
+  if (method.IsInstance) {
+    if (!method.IsCtor)
+      MethodCallStream << "this_ptr->";
+    MethodCallStream << method.BaseName;
+  } else
+    MethodCallStream << method.QualifiedName;
+
+  std::vector<std::string> Args;
+  std::string FunctionPointerName;
+  for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) 
{
+    std::string Arg;
+    // We must check for `const char *` and `const char **` first.
+    if (TypeIsConstCharPtr(Iter->Type)) {
+      // `const char *` is stored server-side as rpc_common::ConstCharPointer
+      Arg = Iter->Name + ".c_str()";
+    } else if (TypeIsConstCharPtrPtr(Iter->Type)) {
+      // `const char **` is stored server-side as rpc_common::StringList
+      Arg = Iter->Name + ".argv()";
+    } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) {
+      Arg = Iter->Name;
+      if (!Iter->Type->isPointerType())
+        Arg = "*" + Iter->Name;
+    } else if (Iter->Type->isPointerType() &&
+               !Iter->Type->isFunctionPointerType() &&
+               (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) {
+      // We move pointers between the server and client as 'Bytes' objects.
+      // Pointers with length arguments will have their length filled in below.
+      // Pointers with no length arguments are assumed to behave like an array
+      // with length of 1, except for void pointers which are handled
+      // differently.
+      Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name +
+            ".GetData()";
+    } else if (Iter->Type->isFunctionPointerType()) {
+      // If we have a function pointer, we only want to pass something along if
+      // we got a real pointer.
+      Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr";
+      FunctionPointerName = Iter->Name;
+    } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen &&
+               method.ContainsFunctionPointerParameter) {
+      // Assumptions:
+      //  - This is assumed to be the baton for the function pointer.
+      //  - This is assumed to come after the function pointer parameter.
+      // We always produce this regardless of the value of the baton argument.
+      Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name +
+            ", connection.GetConnectionID())";
+    } else
+      Arg = Iter->Name;
+
+    if (Iter->Type->isRValueReferenceType())
+      Arg = "std::move(" + Arg + ")";
+    Args.push_back(Arg);
+
+    if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
+        Iter->IsFollowedByLen) {
+      std::string LengthArg = Iter->Name + ".GetSize()";
+      if (!Iter->Type->isVoidPointerType()) {
+        QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type);
+        LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")";
+      }
+      Args.push_back(LengthArg);
+      Iter++;
+    }
+  }
+  MethodCallStream << "(" << llvm::join(Args, ", ") << ")";
+
+  return MethodCall;
+}
+
+std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value,
+                                                     bool IsEncodingSBClass) {
+  std::string EncodeLine;
+  llvm::raw_string_ostream EncodeLineStream(EncodeLine);
+
+  if (IsEncodingSBClass)
+    EncodeLineStream << "RPCServerObjectEncoder(";
+  else
+    EncodeLineStream << "RPCValueEncoder(";
+
+  EncodeLineStream
+      << "response, rpc_common::RPCPacket::ValueType::ReturnValue, ";
+  EncodeLineStream << Value;
+  EncodeLineStream << ");";
+  return EncodeLine;
+}
+
+// There are 4 cases to consider:
+// - const SBClass &: No need to do anything.
+// - const foo &: No need to do anything.
+// - SBClass &: The server and the client hold on to IDs to refer to specific
+//   instances, so there's no need to send any information back to the client.
+// - foo &: The client is sending us a value over the wire, but because the 
type
+//   is mutable, we must send the changed value back in case the method call
+//   mutated it.
+//
+// Updating a mutable reference is done as a return value from the RPC
+// perspective. These return values need to be emitted after the method's 
return
+// value, and they are emitted in the order in which they occur in the
+// declaration.
+void RPCServerSourceEmitter::EmitEncodesForMutableParameters(
+    const std::vector<Param> &Params) {
+  for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) {
+    // No need to manually update an SBClass
+    if (lldb_rpc_gen::TypeIsSBClass(Iter->Type))
+      continue;
+
+    if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType())
+      continue;
+
+    // If we have a void pointer with no length, there's nothing to update. 
This
+    // is likely a baton for a callback. The same goes for function pointers.
+    if (Iter->Type->isFunctionPointerType() ||
+        (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen))
+      continue;
+
+    // No need to update pointers and references to const-qualified data.
+    QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type);
+    if (UnderlyingType.isConstQualified())
+      continue;
+
+    const std::string EncodeLine =
+        CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false);
+    EmitLine(EncodeLine);
+  }
+}
+
+// There are 3 possible scenarios that this method can encounter:
+// 1. The method has no return value and is not a constructor.
+//    Only the method call itself is emitted.
+// 2. The method is a constructor.
+//    The call to the constructor is emitted in the encode line.
+// 3. The method has a return value.
+//    The method call is emitted and the return value is captured in a 
variable.
+//    After that, an encode call is emitted with the variable that captured the
+//    return value.
+void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) {
+  const std::string MethodCall = CreateMethodCall(method);
+
+  // If this function returns nothing, we just emit the call and update any
+  // mutable references. Note that constructors have return type `void` so we
+  // must explicitly check for that here.
+  if (!method.IsCtor && method.ReturnType->isVoidType()) {
+    EmitLine(MethodCall + ";");
+    EmitEncodesForMutableParameters(method.Params);
+    return;
+  }
+
+  static constexpr llvm::StringLiteral ReturnVariableName("__result");
+
+  // If this isn't a constructor, we'll need to store the result of the method
+  // call in a result variable.
+  if (!method.IsCtor) {
+    // We need to determine what the appropriate return type is. Here is the
+    // strategy:
+    // 1.) `SBFoo` -> `SBFoo &&`
+    // 2.) If the type is a pointer other than `const char *` or `const char 
**`
+    //     or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *`
+    //     -> `Bytes`).
+    // 3.) Otherwise, emit the exact same return type.
+    std::string ReturnTypeName;
+    std::string AssignLine;
+    llvm::raw_string_ostream AssignLineStream(AssignLine);
+    if (method.ReturnType->isPointerType() &&
+        !lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) &&
+        !lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) &&
+        !method.ReturnType->isVoidPointerType()) {
+      llvm::StringRef MangledNameRef(method.MangledName);
+      auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef);
+      assert(Pos != MethodsWithPointerReturnTypes.end() &&
+             "Unable to determine the size of the return buffer");
+      if (Pos == MethodsWithPointerReturnTypes.end()) {
+        EmitLine(
+            "// Intentionally inserting a compiler error. lldb-rpc-gen "
+            "was unable to determine how large the return buffer should be.");
+        EmitLine("#error: \"unable to determine size of return buffer\"");
+        return;
+      }
+      AssignLineStream << "Bytes " << ReturnVariableName << "(" << MethodCall
+                       << ", " << Pos->second << ");";
+    } else {
+      if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) {
+        // We want to preserve constness, so we don't strip qualifications from
+        // the underlying type
+        QualType UnderlyingReturnType =
+            lldb_rpc_gen::GetUnderlyingType(method.ReturnType);
+        ReturnTypeName =
+            UnderlyingReturnType.getAsString(method.Policy) + " &&";
+      } else
+        ReturnTypeName = method.ReturnType.getAsString(method.Policy);
+
+      AssignLineStream << ReturnTypeName << " " << ReturnVariableName << " = "
+                       << MethodCall << ";";
+    }
+    EmitLine(AssignLine);
+  }
+
+  const bool IsEncodingSBClass =
+      lldb_rpc_gen::TypeIsSBClass(method.ReturnType) || method.IsCtor;
+
+  std::string ValueToEncode;
+  if (IsEncodingSBClass) {
+    if (method.IsCtor)
+      ValueToEncode = MethodCall;
+    else
+      ValueToEncode = "std::move(" + ReturnVariableName.str() + ")";
+  } else
+    ValueToEncode = ReturnVariableName.str();
+
+  const std::string ReturnValueEncodeLine =
+      CreateEncodeLine(ValueToEncode, IsEncodingSBClass);
+  EmitLine(ReturnValueEncodeLine);
+  EmitEncodesForMutableParameters(method.Params);
+}
+
+// NOTE: This contains most of the same knowledge as RPCLibrarySourceEmitter. I
+// have chosen not to re-use code here because the needs are different enough
+// that it would be more work to re-use than just reimplement portions of it.
+// Specifically:
+//  - Callbacks do not neatly fit into a `Method` object, which currently
+//    assumes that you have a CXXMethodDecl (We have a FunctionDecl at most).
+//  - We only generate callbacks that have a `void *` baton parameter. We 
hijack
+//    those baton parameters and treat them differently.
+//  - Callbacks need to do something special for moving SB class references 
back
+//    to the client-side.
+void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) {
+  // Check invariants and locate necessary resources
+  Param FuncPointerParam;
+  Param BatonParam;
+  for (const auto &Param : method.Params)
+    if (Param.Type->isFunctionPointerType())
+      FuncPointerParam = Param;
+    else if (Param.Type->isVoidPointerType())
+      BatonParam = Param;
+
+  assert(FuncPointerParam.Type->isFunctionPointerType() &&
+         "Emitting callback function with no function pointer");
+  assert(BatonParam.Type->isVoidPointerType() &&
+         "Emitting callback function with no baton");
+
+  QualType FuncType = FuncPointerParam.Type->getPointeeType();
+  const auto *FuncProtoType = FuncType->getAs<FunctionProtoType>();
+  assert(FuncProtoType && "Emitting callback with no parameter information");
+  if (!FuncProtoType)
+    return; // If asserts are off, we'll just fail to compile.
+
+  std::vector<Param> CallbackParams;
+  std::vector<std::string> CallbackParamsAsStrings;
+  uint8_t ArgIdx = 0;
+  for (QualType ParamType : FuncProtoType->param_types()) {
+    Param CallbackParam;
+    CallbackParam.IsFollowedByLen = false;
+    CallbackParam.Type = ParamType;
+    if (ParamType->isVoidPointerType())
+      CallbackParam.Name = "baton";
+    else
+      CallbackParam.Name = "arg" + std::to_string(ArgIdx++);
+
+    CallbackParams.push_back(CallbackParam);
+    CallbackParamsAsStrings.push_back(ParamType.getAsString(method.Policy) +
+                                      " " + CallbackParam.Name);
+  }
+  const std::string CallbackReturnTypeName =
+      FuncProtoType->getReturnType().getAsString(method.Policy);
+  const std::string CallbackName = method.MangledName + "_callback";
+
+  // Emit Function Header
+  std::string Header;
+  llvm::raw_string_ostream HeaderStream(Header);
+  HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName
+               << "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {";
+  EmitLine(Header);
+  IndentLevel++;
+
+  // Emit Function Body
+  EmitLine("// RPC connection setup and sanity checking");
+  EmitLine("CallbackInfo *callback_info = (CallbackInfo *)baton;");
+  EmitLine("rpc_common::ConnectionSP connection_sp = "
+           "rpc_common::Connection::GetConnectionFromID(callback_info->"
+           "connection_id);");
+  EmitLine("if (!connection_sp)");
+  IndentLevel++;
+  if (FuncProtoType->getReturnType()->isVoidType())
+    EmitLine("return;");
+  else
+    EmitLine("return {};");
+  IndentLevel--;
+
+  EmitLine("// Preparing to make the call");
+  EmitLine("static RPCFunctionInfo g_func(\"" + CallbackName + "\");");
+  EmitLine("RPCStream send;");
+  EmitLine("RPCStream response;");
+  EmitLine("g_func.Encode(send);");
+
+  EmitLine("// The first thing we encode is the callback address so that the "
+           "client-side can know where the callback is");
+  EmitLine("RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, "
+           "callback_info->callback);");
+  EmitLine("// Encode all the arguments");
+  for (const Param &CallbackParam : CallbackParams) {
+    if (lldb_rpc_gen::TypeIsSBClass(CallbackParam.Type)) {
+
+      // FIXME: SB class server references are stored as non-const references 
so
+      // that we can actually change them as needed. If a parameter is marked
+      // const, we will fail to compile because we cannot make an
+      // SBFooServerReference from a `const SBFoo &`.
+      // To work around this issue, we'll apply a `const_cast` if needed so we
+      // can continue to generate callbacks for now, but we really should
+      // rethink the way we store object IDs server-side to support
+      // const-qualified parameters.
+      QualType UnderlyingSBClass =
+          lldb_rpc_gen::GetUnderlyingType(CallbackParam.Type);
+      QualType UnqualifiedUnderlyingSBClass =
+          UnderlyingSBClass.getUnqualifiedType();
+
+      std::string SBClassName = GetSBClassNameFromType(UnderlyingSBClass);
+      llvm::StringRef SBClassNameRef(SBClassName);
+      SBClassNameRef.consume_front("lldb::");
+
+      std::string ServerReferenceLine;
+      llvm::raw_string_ostream ServerReferenceLineStream(ServerReferenceLine);
+      ServerReferenceLineStream << "rpc_server::" << SBClassNameRef
+                                << "ServerReference " << CallbackParam.Name
+                                << "_ref(";
+
+      if (UnderlyingSBClass.isConstQualified()) {
+        QualType NonConstSBType =
+            
method.Context.getLValueReferenceType(UnqualifiedUnderlyingSBClass);
+        ServerReferenceLineStream << "const_cast<" << NonConstSBType << ">(";
+      }
+      ServerReferenceLineStream << CallbackParam.Name;
+      if (UnderlyingSBClass.isConstQualified())
+        ServerReferenceLineStream << ")";
+
+      ServerReferenceLineStream << ");";
+      EmitLine(ServerReferenceLine);
+      EmitLine(
+          CallbackParam.Name +
+          "_ref.Encode(send, rpc_common::RPCPacket::ValueType::Argument);");
+    } else {
+      std::string ParamName;
+      if (CallbackParam.Type->isVoidPointerType())
+        ParamName = "callback_info->baton";
+      else
+        ParamName = CallbackParam.Name;
+      EmitLine(
+          "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " 
+
+          ParamName + ");");
+    }
+  }
+
+  if (!FuncProtoType->getReturnType()->isVoidType()) {
+    EmitLine("// Storage for return value");
+    const bool ReturnsSBClass =
+        lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType());
+    std::string ReturnValueLine = CallbackReturnTypeName;
+    llvm::raw_string_ostream ReturnValueLineStream(ReturnValueLine);
+
+    if (ReturnsSBClass)
+      ReturnValueLineStream << " *";
+    ReturnValueLineStream << " __result = ";
+    if (ReturnsSBClass)
+      ReturnValueLineStream << "nullptr";
+    else
+      ReturnValueLineStream << "{}";
+    ReturnValueLineStream << ";";
+    EmitLine(ReturnValueLine);
+  }
+
+  EmitLine(
+      "if (connection_sp->SendRPCCallAndWaitForResponse(send, response)) {");
+  IndentLevel++;
+  if (!FuncProtoType->getReturnType()->isVoidType()) {
+    if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType())) {
+      EmitLine("__result = rpc_server::RPCServerObjectDecoder<" +
+               CallbackReturnTypeName +
+               ">(response, rpc_common::RPCPacket::ValueType::ReturnValue);");
+    } else
+      EmitLine("RPCValueDecoder(response, "
+               "rpc_common::RPCPacket::ValueType::ReturnValue, __result);");
+  }
+  IndentLevel--;
+  EmitLine("}");
+  if (!FuncProtoType->getReturnType()->isVoidType()) {
+    if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType()))
+      EmitLine("return *__result;");
+    else
+      EmitLine("return __result;");
+  }
+
+  // Emit Function Footer;
+  IndentLevel--;
+  EmitLine("};");
+}
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h
new file mode 100644
index 0000000000000..a371dc100ff7a
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h
@@ -0,0 +1,81 @@
+//===-- RPCServerSourceEmitter.h 
------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef LLDB_RPC_GEN_RPCSERVERMETHODEMITTER_H
+#define LLDB_RPC_GEN_RPCSERVERMETHODEMITTER_H
+
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace lldb_rpc_gen {
+/// Emit the source code for server-side *.cpp files.
+class RPCServerSourceEmitter : public FileEmitter {
+public:
+  RPCServerSourceEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile)
+      : FileEmitter(std::move(OutputFile)) {
+    Begin();
+  }
+
+  /// Given a Method, emits a server-side implementation of the method
+  /// for lldb-rpc-server
+  void EmitMethod(const Method &method);
+
+private:
+  void EmitCommentHeader(const Method &method);
+
+  void EmitFunctionHeader(const Method &method);
+
+  void EmitFunctionBody(const Method &method);
+
+  void EmitFunctionFooter();
+
+  void EmitStorageForParameters(const Method &method);
+
+  void EmitStorageForOneParameter(QualType ParamType,
+                                  const std::string &ParamName,
+                                  const PrintingPolicy &Policy,
+                                  bool IsFollowedByLen);
+
+  void EmitDecodeForParameters(const Method &method);
+
+  void EmitDecodeForOneParameter(QualType ParamType,
+                                 const std::string &ParamName,
+                                 const PrintingPolicy &Policy);
+
+  std::string CreateMethodCall(const Method &method);
+
+  std::string CreateEncodeLine(const std::string &value,
+                               bool IsEncodingSBClass);
+
+  void EmitEncodesForMutableParameters(const std::vector<Param> &Params);
+
+  void EmitMethodCallAndEncode(const Method &method);
+
+  void EmitCallbackFunction(const Method &method);
+
+  void Begin() {
+    EmitLine("#include \"RPCUserServer.h\"");
+    EmitLine("#include \"SBAPI.h\"");
+    EmitLine("#include <lldb-rpc/common/RPCArgument.h>");
+    EmitLine("#include <lldb-rpc/common/RPCCommon.h>");
+    EmitLine("#include <lldb-rpc/common/RPCFunction.h>");
+    EmitLine("#include <lldb/API/LLDB.h>");
+    EmitLine("");
+    EmitLine("using namespace rpc_common;");
+    EmitLine("using namespace lldb;");
+  }
+};
+} // namespace lldb_rpc_gen
+
+#endif // LLDB_RPC_GEN_RPCSERVERMETHODEMITTER_H

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to