================
@@ -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++) 
{
+    // SBTarget::BreakpointCreateByNames specifically uses an
+    // rpc_common::StringList when encoding the list of symbol names in the
+    // handwritten version. This allows it to account for null terminators and
+    // without it, Xcode crashes when calling this function. The else-if block
+    // below replaces params that have a pointer and length with a Bytes 
object.
+    // We can use the same logic in order to replace const char **s with
+    // StringLists
+    //
+    // rdar://146976130
+    if (lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
+        Iter->IsFollowedByLen) {
+      std::string StringListLine;
+      const std::string StringListName = Iter->Name + "_list";
+      StringListLine = "StringList " + StringListName + "(" + Iter->Name + ", 
";
+      Iter++;
+      StringListLine += Iter->Name + ");";
+      Iter--;
+      EmitLine(StringListLine);
+      EmitLine(
+          "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " 
+
+          StringListName + ");");
+      // When we have pointer parameters, in general the strategy is
+      // to create `Bytes` objects from them (basically a buffer with a size)
+      // and then move those over the wire. We're not moving the pointer 
itself,
+      // but the contents of memory being pointed to. There are a few 
exceptions
+      // to this:
+      //   - If the type is `const char *` or `const char **`, those are 
handled
+      //     specially and can be encoded directly.
+      //   - If we have a function pointer, we move the pointer value directly.
+      //     To do the callback from the server-side, we will need this pointer
+      //     value to correctly invoke the function client-side.
+      //   - If we have a baton (to support a callback), we need to move the
+      //     pointer value directly. This is for the same reason as callbacks
+      //     above.
+      //   - If we have a pointer to an SB class, we just dereference it and
+      //     encode it like a normal SB class object.
----------------
chelcassanova wrote:

I'm actually going to move this comment to be inside of the else if block below 
and match it with the comment that's already in that block.

https://github.com/llvm/llvm-project/pull/147655
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to