https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/147655
>From b842db905bf10fd6b89f2dfc07cd6a1eaa2bfd7b Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova <chelsea_cassan...@apple.com> Date: Wed, 2 Jul 2025 00:17:31 -0700 Subject: [PATCH] [lldb][rpc] Upstream RPC Client Library Emitters This commit upstreams the client side emitters for the lldb-rpc-gen tool alongside tests for its functionality and heuristics. https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804 --- .../Inputs/Client/CheckArrayPointer.h | 17 + .../Client/CheckConstCharPtrPtrWithLen.h | 19 + .../Generator/Inputs/Client/CheckConstSBRef.h | 19 + .../Inputs/Client/CheckNonConstSBRef.h | 19 + .../Generator/Inputs/Client/CheckSBPointer.h | 18 + .../Generator/Inputs/Client/CheckVoidPtr.h | 18 + .../Tests/Client/CheckArrayPointer.test | 12 + .../Client/CheckConstCharPtrPtrWithLen.test | 11 + .../Tests/Client/CheckConstSBRef.test | 15 + .../Tests/Client/CheckNonConstSBRef.test | 14 + .../Tests/Client/CheckSBPointer.test | 17 + .../Generator/Tests/Client/CheckVoidPtr.test | 11 + .../client/RPCLibraryHeaderEmitter.cpp | 126 ++++ .../client/RPCLibraryHeaderEmitter.h | 44 ++ .../client/RPCLibrarySourceEmitter.cpp | 541 ++++++++++++++++++ .../client/RPCLibrarySourceEmitter.h | 92 +++ .../lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp | 99 +++- 17 files changed, 1084 insertions(+), 8 deletions(-) create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test create mode 100644 lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.h diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h new file mode 100644 index 0000000000000..65feabbf9c89b --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h @@ -0,0 +1,17 @@ +#ifndef LLDB_API_SBRPC_CHECKARRAYPTR_H +#define LLDB_API_SBRPC_CHECKARRAYPTR_H + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CheckArrayPtr { +public: + // Pointers to arrays followed by length must use a + // Bytes object constructed using that pointer and the sizeof() + // the array object. + int CheckArrayPtr(uint64_t *array, size_t array_len); + +}; // class SBRPC_CheckArrayPtr +} // namespace lldb + +#endif // LLDB_API_SBRPC_CheckArrayPtr_H diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h new file mode 100644 index 0000000000000..ab0d6537011fa --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h @@ -0,0 +1,19 @@ +#ifndef LLDB_API_SBRPC_CHECKCONSTCHARPTRPTRWITHLEN_H +#define LLDB_API_SBRPC_CHECKCONSTCHARPTRPTRWITHLEN_H + +#include <cstddef> +#include <cstdio> + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CheckConstCharPtrPtrWithLen { +public: + // const char ** followed by len must use a StringList + // when being encoded. + int CheckConstCharPtrPtrWithLen(const char **arg1, size_t len); + +}; // class SBRPC_CheckConstCharPtrPtrWithLen +} // namespace lldb + +#endif // LLDB_API_SBRPC_CheckConstCharPtrPtrWithLen_H diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h new file mode 100644 index 0000000000000..153fdad60a494 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h @@ -0,0 +1,19 @@ +#ifndef LLDB_API_SBRPC_CHECKCONSTSBREF_H +#define LLDB_API_SBRPC_CHECKCONSTSBREF_H + +#include <cstddef> +#include <cstdio> + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CHECKCONSTSBREF { +public: + // Const references to SB classes should be encoded as usual without + // needing to create a new object with its own connection. + int CheckConstSBRef(const SBDebugger &debugger_ref); + +}; // class SBRPC_CHECKCONSTSBREF +} // namespace lldb + +#endif // LLDB_API_SBRPC_CHECKCONSTSBREF_H diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h new file mode 100644 index 0000000000000..3335157418a3f --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h @@ -0,0 +1,19 @@ +#ifndef LLDB_API_SBRPC_CHECKNONCONSTSBREF_H +#define LLDB_API_SBRPC_CHECKNONCONSTSBREF_H + +#include <cstddef> + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CheckNonConstSBRef { +public: + // Non-const references to SB classes will have new objects + // of that class constructed with the connection as the first parameter + // before being encoded if their existing connection is invalid. + int CheckNonConstSBRef(SBDebugger &debugger_ref); + +}; // class SBRPC_CheckNonConstSBRef +} // namespace lldb + +#endif // LLDB_API_SBRPC_CheckNonConstSBRef_H diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h new file mode 100644 index 0000000000000..ce7915598c71c --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h @@ -0,0 +1,18 @@ +#ifndef LLDB_API_SBRPC_CHECKSBPTR_H +#define LLDB_API_SBRPC_CHECKSBPTR_H + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CheckSBPtr { +public: + // Pointers to SB objects must be checked to + // see if they're null. If so, then a new object of the given + // class must be created and encoded. Otherwise, the original + // parameter will be encoded. + int CheckSBPtr(SBDebugger *debugger_ptr); + +}; // class SBRPC_CHECKSBPtr +} // namespace lldb + +#endif // LLDB_API_SBRPC_CHECKSBPtr_H diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h new file mode 100644 index 0000000000000..c1cd3d0f92532 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h @@ -0,0 +1,18 @@ +#ifndef LLDB_API_SBRPC_CHECKVOIDPTR_H +#define LLDB_API_SBRPC_CHECKVOIDPTR_H + +#include <cstddef> + +#include "lldb/API/SBDefines.h" + +namespace lldb { +class LLDB_API SBRPC_CheckVoidPtr { +public: + // void * followed by length must use a Bytes object + // when being encoded. + int CheckVoidPtr(void *buf, size_t len); + +}; // class SBRPC_CheckVoidPtr +} // namespace lldb + +#endif // LLDB_API_SBRPC_CheckVoidPtr_H diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test new file mode 100644 index 0000000000000..6e5ef28c6c024 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test @@ -0,0 +1,12 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/Client/CheckArrayPointer.h + +RUN: cat %t/lib/CheckArrayPointer.cpp | FileCheck %s + +# Pointers to arrays followed by length must use a +# Bytes object constructed using that pointer and the sizeof() +# the array object. +CHECK: Bytes array_buffer(array, sizeof(uint64_t)); diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test new file mode 100644 index 0000000000000..9fff2a84a6990 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test @@ -0,0 +1,11 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckConstCharPtrPtrWithLen.h + +RUN: cat %t/lib/CheckConstCharPtrPtrWithLen.cpp | FileCheck %s + +# const char ** followed by len must use a StringList +# when being encoded. +CHECK: StringList arg1_list diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test new file mode 100644 index 0000000000000..0c2935e767a32 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test @@ -0,0 +1,15 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/Client/CheckConstSBRef.h + +RUN: cat %t/lib/CheckConstSBRef.cpp | FileCheck %s + +# Const references to SB classes should be encoded as usual without +# needing to create a new object with its own connection. Here +# we're checking to make sure that the given SB object ref will get +# encoded immediately after the previous argument gets encoded without +# anything happening in between. +CHECK: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, *this); +CHECK-NEXT: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, debugger_ref) diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test new file mode 100644 index 0000000000000..547277ea6fc0c --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test @@ -0,0 +1,14 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/Client/CheckNonConstSBRef.h + +RUN: cat %t/lib/CheckNonConstSBRef.cpp | FileCheck %s + +# Non-const references to SB classes will have new objects +# of that class constructed with the connection as the first parameter +# before being encoded if their existing connection is invalid. +CHECK: if (connection_sp && !debugger_ref.ObjectRefIsValid()) +CHECK-NEXT: debugger_ref = lldb_rpc::SBDebugger(connection_sp); +CHECK-NEXT: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, debugger_ref); diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test new file mode 100644 index 0000000000000..bc86bab93243e --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test @@ -0,0 +1,17 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/Client/CheckSBPointer.h + +RUN: cat %t/lib/CheckSBPointer.cpp +RUN: cat %t/lib/CheckSBPointer.cpp | FileCheck %s + +# Pointers to SB objects must be checked to +# see if they're null. If so, then a new object of the given +# class must be created and encoded. Otherwise, the original +# parameter will be encoded. +CHECK: if (debugger_ptr) +CHECK-NEXT: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, *debugger_ptr); +CHECK-NEXT: else +CHECK-NEXT: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, rpc::ObjectRef(ObjectRefGetConnectionID(), eClass_lldb_SBDebugger, LLDB_RPC_INVALID_OBJECT_ID)); diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test new file mode 100644 index 0000000000000..6598fc50cfc34 --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test @@ -0,0 +1,11 @@ +# Disabling until the lldb-rpc-gen tool lands. +XFAIL: * +RUN: mkdir -p %t/server +RUN: mkdir -p %t/lib +RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/Client/CheckVoidPtr.h + +RUN: cat %t/lib/CheckVoidPtr.cpp | FileCheck %s + +# void * followed by length must use a Bytes object +# when being encoded. +CHECK: Bytes buf_buffer(buf, len); diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp new file mode 100644 index 0000000000000..a8c8645d1000d --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp @@ -0,0 +1,126 @@ +#include "RPCLibraryHeaderEmitter.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 RPCLibraryHeaderEmitter::StartClass(std::string ClassName) { + CurrentClass = std::move(ClassName); + std::string BaseClass = + lldb_rpc_gen::SBClassInheritsFromObjectRef(CurrentClass) + ? "ObjectRef" + : "LocalObjectRef"; + EmitLine("class " + CurrentClass + " : public rpc::" + BaseClass + " {"); + EmitLine("public:"); + IndentLevel++; + if (lldb_rpc_gen::SBClassRequiresDefaultCtor(CurrentClass)) + EmitLine(CurrentClass + "();"); + + // NOTE: There's currently only one RPC-specific extension that is actually + // used AFAICT. We can generalize this if we need more. + if (CurrentClass == "SBDebugger") + EmitLine("int SetIOFile(const char *path);"); +} + +void RPCLibraryHeaderEmitter::EndClass() { + if (lldb_rpc_gen::SBClassRequiresCopyCtorAssign(CurrentClass)) { + if (!CopyCtorEmitted) + EmitLine(CurrentClass + "(const lldb_rpc::" + CurrentClass + " &rhs);"); + if (!CopyAssignEmitted) + EmitLine(CurrentClass + " &operator=(const lldb_rpc::" + CurrentClass + + " &rhs);"); + } + if (!MoveCtorEmitted) + EmitLine(CurrentClass + "(lldb_rpc::" + CurrentClass + " &&rhs);"); + if (!MoveAssignEmitted) + EmitLine(CurrentClass + " &operator=(" + CurrentClass + " &&rhs);"); + + IndentLevel--; + EmitLine("}; // class " + CurrentClass); + CurrentClass.clear(); +} + +void RPCLibraryHeaderEmitter::EmitMethod(const Method &method) { + std::string DeclarationLine; + llvm::raw_string_ostream DeclarationLineStream(DeclarationLine); + + if (method.IsExplicitCtorOrConversionMethod) + DeclarationLineStream << "explicit "; + else if (!method.IsInstance) + DeclarationLineStream << "static "; + + if (!method.IsDtor && !method.IsConversionMethod && !method.IsCtor) { + DeclarationLineStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + method.ReturnType.getAsString(method.Policy)) + << " "; + } + + DeclarationLineStream << method.BaseName << "(" + << method.CreateParamListAsString( + eLibrary, /*IncludeDefaultValue = */ true) + << ")"; + if (method.IsConst) + DeclarationLineStream << " const"; + DeclarationLineStream << ";"; + + EmitLine(DeclarationLine); + + if (method.IsCopyCtor) + CopyCtorEmitted = true; + else if (method.IsCopyAssign) + CopyAssignEmitted = true; + else if (method.IsMoveCtor) + MoveCtorEmitted = true; + else if (method.IsMoveAssign) + MoveAssignEmitted = true; +} + +void RPCLibraryHeaderEmitter::EmitEnum(EnumDecl *E) { + // NOTE: All of the enumerations embedded in SB classes are currently + // anonymous and backed by an unsigned int. + EmitLine("enum : unsigned {"); + IndentLevel++; + for (const EnumConstantDecl *EC : E->enumerators()) { + std::string EnumValue = EC->getNameAsString(); + SmallString<16> ValueStr; + EC->getInitVal().toString(ValueStr); + EnumValue += " = " + std::string(ValueStr) + ", "; + EmitLine(EnumValue); + } + + IndentLevel--; + EmitLine("};"); +} + +std::string RPCLibraryHeaderEmitter::GetHeaderGuard() { + const std::string UpperFilenameNoExt = + llvm::sys::path::stem( + llvm::sys::path::filename(OutputFile->getFilename())) + .upper(); + return "GENERATED_LLDB_RPC_LIBRARY_" + UpperFilenameNoExt + "_H"; +} + +void RPCLibraryHeaderEmitter::Begin() { + const std::string HeaderGuard = GetHeaderGuard(); + EmitLine("#ifndef " + HeaderGuard); + EmitLine("#define " + HeaderGuard); + EmitLine(""); + EmitLine("#include <lldb-rpc/common/RPCPublic.h>"); + EmitLine("#include \"SBDefines.h\""); + EmitLine("#include \"LLDBRPC.h\""); + EmitLine(""); + EmitLine("namespace lldb_rpc {"); +} + +void RPCLibraryHeaderEmitter::End() { + EmitLine("} // namespace lldb_rpc"); + EmitLine("#endif // " + GetHeaderGuard()); +} diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h new file mode 100644 index 0000000000000..b9f22585c7955 --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h @@ -0,0 +1,44 @@ +#ifndef LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H +#define LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H + +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace clang; + +namespace lldb_rpc_gen { + +class RPCLibraryHeaderEmitter : public FileEmitter { +public: + RPCLibraryHeaderEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile) + : FileEmitter(std::move(OutputFile)), CurrentClass() { + Begin(); + } + + ~RPCLibraryHeaderEmitter() { End(); } + + void StartClass(std::string ClassName); + + void EndClass(); + + void EmitMethod(const Method &method); + + void EmitEnum(EnumDecl *E); + +private: + std::string GetHeaderGuard(); + + void Begin(); + + void End(); + + std::string CurrentClass; + bool CopyCtorEmitted = false; + bool CopyAssignEmitted = false; + bool MoveCtorEmitted = false; + bool MoveAssignEmitted = false; +}; +} // namespace lldb_rpc_gen +#endif // LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp new file mode 100644 index 0000000000000..2e290c7a23f8f --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp @@ -0,0 +1,541 @@ +#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) { + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); + + 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; + } +} + +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()) { + // 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; + } else { + // 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; + } + } + } + + 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 + ", "; + StringListLine += std::next(Iter)->Name + ");"; + EmitLine(StringListLine); + EmitLine( + "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + + StringListName + ");"); + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen) && + !lldb_rpc_gen::TypeIsConstCharPtr(Iter->Type) && + !lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + !lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + // 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. + // + // We're aiming for this transformation: + // (TYPE *buf, SIZE len) -> Bytes(buf, sizeof(TYPE) * len) + // The `sizeof` portion is dropped when TYPE is `void`. When there is no + // length argument, we implicitly assume that `len` is 1. + const std::string BufferName = Iter->Name + "_buffer"; + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + std::string BufferLine = "Bytes " + BufferName + "(" + Iter->Name + ", "; + if (!Iter->Type->isVoidPointerType()) + BufferLine += "sizeof(" + + ReplaceLLDBNamespaceWithRPCNamespace( + UnderlyingType.getAsString(method.Policy)) + + ")"; + + if (Iter->IsFollowedByLen && !Iter->Type->isVoidPointerType()) + BufferLine += " * "; + + if (Iter->IsFollowedByLen) { + Iter++; + BufferLine += Iter->Name; + } + + BufferLine += ");"; + EmitLine(BufferLine); + EmitLine("RPCValueEncoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + BufferName + ");"); + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type) && + Iter->Type->isPointerType()) { + // If we have a pointer to an SB class, the strategy is to check for + // nullptr. If we have a valid pointer, we just encode the actual SB class + // itself. Otherwise, we'll need to create a blank one and send that + // along. + // Note: Currently all methods that take SB class pointers are instance + // methods. This assertion will fail if that changes. + assert(method.IsInstance && + "Assumption that only instance methods have pointers to SB class " + "as parameters is no longer true. Please update this logic."); + EmitLine("if (" + Iter->Name + ")"); + IndentLevel++; + EmitLine("RPCValueEncoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, *" + + Iter->Name + ");"); + IndentLevel--; + EmitLine("else"); + IndentLevel++; + // FIXME: change this logic, not everything can be an rpc::ObjectRef!!! + const std::string ClassIdentifier = + "eClass_lldb_" + lldb_rpc_gen::GetSBClassNameFromType(Iter->Type); + EmitLine( + "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + "rpc::ObjectRef(ObjectRefGetConnectionID(), " + + ClassIdentifier + ", LLDB_RPC_INVALID_OBJECT_ID));"); + IndentLevel--; + } else { + const std::string CallbackCast = Iter->Type->isFunctionPointerType() + ? "(rpc_common::function_ptr_t)" + : ""; + + // NOTE: We're going to assume that SB objects are coming in with a valid + // connection and we'll assert if they don't have one. + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + std::string TypeName = lldb_rpc_gen::GetSBClassNameFromType(Iter->Type); + EmitLine("assert(" + Iter->Name + + ".ObjectRefIsValid() && \"SB object refs must be valid before " + "encoding\");"); + } + + EmitLine( + "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + + CallbackCast + Iter->Name + ");"); + } + } +} + +void RPCLibrarySourceEmitter::EmitSendRPCCall(const Method &method) { + EmitLine( + "if (!connection_sp->SendRPCCallAndWaitForResponse(send, response))"); + IndentLevel++; + if (method.ReturnType->isVoidType() || method.IsCtor) + EmitLine("return;"); + else if (method.ReturnType->isPointerType()) + EmitLine("return nullptr;"); + else + EmitLine("return __result;"); + IndentLevel--; + if (CustomLogicForMethods.lookup(method.MangledName).Location == + CustomLogicLocation::eAfterRPCCall) + EmitCustomLogic(method); +} + +void RPCLibrarySourceEmitter::EmitDecodeReturnValues(const Method &method) { + if (!method.ReturnType->isVoidType()) { + std::string DecodeReturnValueLine = + "RPCValueDecoder(response, " + "rpc_common::RPCPacket::ValueType::ReturnValue, " + + ReturnVariableName.str() + ");"; + EmitLine(DecodeReturnValueLine); + } else if (method.IsCtor) + EmitLine("RPCValueDecoder(response, " + "rpc_common::RPCPacket::ValueType::ReturnValue, *this);"); + + // Update mutable parameters (references and pointers) + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + // If what we have is not a reference type or a pointer type, we can safely + // skip this. + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // No need to update SB class instances on the client-side. + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + // We skip over function pointers and their accompanying baton parameters. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // We cannot update const-qualified parameters. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + // This is specific to pointers, but we need to get to the innermost type to + // get the qualification. For references, this loop will never execute. + while (UnderlyingType->isPointerType()) + UnderlyingType = lldb_rpc_gen::GetUnderlyingType(UnderlyingType); + + if (UnderlyingType.isConstQualified()) + continue; + + if (Iter->Type->isReferenceType()) + EmitLine("RPCValueDecoder(response, " + "rpc_common::RPCPacket::ValueType::ReturnValue, " + + Iter->Name + ");"); + else { + assert(Iter->Type->isPointerType() && + "Mutable parameter is not reference or pointer!"); + const std::string &PointerParameterName = Iter->Name; + const std::string BufferName = PointerParameterName + "_buffer"; + std::string SizeExpression; + + // If we have a `void *` with a length parameter, we are counting bytes. + // No `sizeof` will be needed. + if (!Iter->Type->isVoidPointerType()) { + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + SizeExpression = "sizeof(" + + ReplaceLLDBNamespaceWithRPCNamespace( + UnderlyingType.getAsString(method.Policy)) + + ")"; + } + + if (Iter->IsFollowedByLen) { + // If we have a sizeof, we must multiply by the length argument. + if (!SizeExpression.empty()) + SizeExpression += " * "; + Iter++; + SizeExpression += Iter->Name; + } + + EmitLine("RPCValueDecoder(response, " + "rpc_common::RPCPacket::ValueType::ReturnValue, " + + BufferName + ");"); + EmitLine("assert(" + BufferName + ".GetSize() == " + SizeExpression + + " && \"Buffer was resized during RPC call\");"); + // NOTE: We can just treat the pointers as `void *` and copy all the bytes + // needed. + EmitLine("memcpy(" + PointerParameterName + ", " + BufferName + + ".GetData(), " + BufferName + ".GetSize());"); + } + } + + if (CustomLogicForMethods.lookup(method.MangledName).Location == + CustomLogicLocation::eAfterDecode) + EmitCustomLogic(method); + if (!method.ReturnType->isVoidType()) { + // FIXME: Find a solution that does not involve leaking memory. + // We have to persist the buffer we returned somewhere. We stick it in the + // RPCStringPool for now + if (method.ReturnType->isPointerType()) { + std::string ReturnExpression = + "return (" + + lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + method.ReturnType.getAsString(method.Policy)) + + ")RPCStringPool::Add(" + ReturnVariableName.str() + ");"; + EmitLine(ReturnExpression); + } else + EmitLine("return __result;"); + } +} + +void RPCLibrarySourceEmitter::EmitCustomLogic(const Method &method) { + assert(CustomLogicForMethods.contains(method.MangledName) && + "Cannot emit custom logic for method that is not present in custom " + "logic map."); + EmitLine("// Custom logic:"); + EmitLine(CustomLogicForMethods.lookup(method.MangledName).Code); +} diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.h b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.h new file mode 100644 index 0000000000000..be37c82f5f46e --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.h @@ -0,0 +1,92 @@ +#ifndef LLDB_RPC_GEN_RPCLIBRARYSOURCEEMITTER_H +#define LLDB_RPC_GEN_RPCLIBRARYSOURCEEMITTER_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 { +class RPCLibrarySourceEmitter : public FileEmitter { +public: + RPCLibrarySourceEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile) + : FileEmitter(std::move(OutputFile)) { + Begin(); + } + + enum class CustomLogicLocation { + eNone, + eAfterSetup, + eAfterRPCCall, + eAfterDecode + }; + + struct CustomLogic { + CustomLogicLocation Location; + std::string Code; + }; + + void StartClass(std::string ClassName); + + void EndClass(); + + void EmitMethod(const Method &method); + + void EmitEmptyConstructor(const std::string &ClassName); + +private: + void EmitCopyCtor(); + + void EmitCopyAssign(); + + void EmitMoveCtor(); + + void EmitMoveAssign(); + + void EmitCommentHeader(const Method &method); + + void EmitFunctionHeader(const Method &method); + + void EmitFunctionBody(const Method &method); + + void EmitFunctionFooter(); + + void EmitFunctionSetup(const Method &method); + + void EmitReturnValueStorage(const Method &method); + + void EmitConnectionSetup(const Method &method); + + void EmitEncodeParameters(const Method &method); + + void EmitSendRPCCall(const Method &method); + + void EmitDecodeReturnValues(const Method &method); + + void EmitCustomLogic(const Method &method); + + void Begin() { + EmitLine("#include <lldb-rpc/common/RPCArgument.h>"); + EmitLine("#include <lldb-rpc/common/RPCCommon.h>"); + EmitLine("#include <lldb-rpc/common/RPCFunction.h>"); + EmitLine("#include <lldb-rpc/common/RPCStringPool.h>"); + EmitLine("#include <lldb-rpc/liblldbrpc/RPCUserClient.h>"); + EmitLine("#include \"LLDBRPC.h\""); + EmitLine("#include <cassert>"); + EmitLine("using namespace rpc_common;"); + EmitLine("using namespace lldb_rpc;"); + } + + std::string CurrentClass; + bool CopyCtorEmitted = false; + bool CopyAssignEmitted = false; + bool MoveCtorEmitted = false; + bool MoveAssignEmitted = false; +}; +} // namespace lldb_rpc_gen + +#endif // LLDB_RPC_GEN_RPCLIBRARYSOURCEEMITTER_H diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp index e6b601ea13012..747dc95561bef 100644 --- a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp @@ -7,8 +7,10 @@ //===----------------------------------------------------------------------===// #include "RPCCommon.h" -#include "RPCServerHeaderEmitter.h" -#include "RPCServerSourceEmitter.h" +#include "client/RPCLibraryHeaderEmitter.h" +#include "client/RPCLibrarySourceEmitter.h" +#include "server/RPCServerHeaderEmitter.h" +#include "server/RPCServerSourceEmitter.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" @@ -46,6 +48,12 @@ static std::string GetLibraryOutputDirectory() { return std::string(Path); } +static std::string GetServerOutputDirectory() { + llvm::SmallString<128> Path(OutputDir.getValue()); + llvm::sys::path::append(Path, "server"); + return std::string(Path); +} + static std::unique_ptr<llvm::ToolOutputFile> CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { llvm::SmallString<128> Path(OutputDir); @@ -79,10 +87,14 @@ class SBVisitor : public RecursiveASTVisitor<SBVisitor> { SBVisitor(GeneratedByproducts &Byproducts, SourceManager &Manager, ASTContext &Context, std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile, - std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile) + std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile, + std::unique_ptr<llvm::ToolOutputFile> &&LibrarySourceOutputFile, + std::unique_ptr<llvm::ToolOutputFile> &&LibraryHeaderOutputFile) : Byproducts(Byproducts), Manager(Manager), Context(Context), ServerSourceEmitter(std::move(ServerMethodOutputFile)), - ServerHeaderEmitter(std::move(ServerHeaderOutputFile)) {} + ServerHeaderEmitter(std::move(ServerHeaderOutputFile)), + LibrarySourceEmitter(std::move(LibrarySourceOutputFile)), + LibraryHeaderEmitter(std::move(LibraryHeaderOutputFile)) {} ~SBVisitor() {} @@ -97,10 +109,17 @@ class SBVisitor : public RecursiveASTVisitor<SBVisitor> { PrintingPolicy Policy(Context.getLangOpts()); Policy.Bool = true; + LibraryHeaderEmitter.StartClass(ClassName); + LibrarySourceEmitter.StartClass(ClassName); + for (Decl *D : RDecl->decls()) + if (auto *E = dyn_cast_or_null<EnumDecl>(D)) + LibraryHeaderEmitter.EmitEnum(E); + for (CXXMethodDecl *MDecl : RDecl->methods()) { const std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); - const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(MangledName); + const bool IsDisallowed = + lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); const bool HasCallbackParameter = lldb_rpc_gen::HasCallbackParameter(MDecl); SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl); @@ -108,10 +127,14 @@ class SBVisitor : public RecursiveASTVisitor<SBVisitor> { const lldb_rpc_gen::Method Method(MDecl, Policy, Context); ServerSourceEmitter.EmitMethod(Method); ServerHeaderEmitter.EmitMethod(Method); + LibrarySourceEmitter.EmitMethod(Method); + LibraryHeaderEmitter.EmitMethod(Method); Byproducts.MangledMethodNames.insert(MangledName); } else if (MethodSupportLevel == eUnimplemented) Byproducts.SkippedMethodNames.insert(MangledName); } + LibraryHeaderEmitter.EndClass(); + LibrarySourceEmitter.EndClass(); return true; } @@ -211,6 +234,8 @@ class SBVisitor : public RecursiveASTVisitor<SBVisitor> { ASTContext &Context; lldb_rpc_gen::RPCServerSourceEmitter ServerSourceEmitter; lldb_rpc_gen::RPCServerHeaderEmitter ServerHeaderEmitter; + lldb_rpc_gen::RPCLibrarySourceEmitter LibrarySourceEmitter; + lldb_rpc_gen::RPCLibraryHeaderEmitter LibraryHeaderEmitter; }; class SBConsumer : public ASTConsumer { @@ -218,9 +243,13 @@ class SBConsumer : public ASTConsumer { SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager, ASTContext &Context, std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile, - std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile) + std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile, + std::unique_ptr<llvm::ToolOutputFile> &&LibrarySourceOutputFile, + std::unique_ptr<llvm::ToolOutputFile> &&LibraryHeaderOutputFile) : Visitor(Byproducts, Manager, Context, std::move(ServerMethodOutputFile), - std::move(ServerHeaderOutputFile)) {} + std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), + std::move(LibraryHeaderOutputFile)) {} bool HandleTopLevelDecl(DeclGroupRef DR) override { for (Decl *D : DR) Visitor.TraverseDecl(D); @@ -255,11 +284,26 @@ class SBAction : public ASTFrontendAction { if (!ServerHeaderOutputFile) return nullptr; + const std::string LibrarySourceFilename = FilenameNoExt.str() + ".cpp"; + std::unique_ptr<llvm::ToolOutputFile> LibrarySourceOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibrarySourceFilename); + if (!LibrarySourceOutputFile) + return nullptr; + + const std::string LibraryHeaderFilename = FilenameNoExt.str() + ".h"; + std::unique_ptr<llvm::ToolOutputFile> LibraryHeaderOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibraryHeaderFilename); + if (!LibraryHeaderOutputFile) + return nullptr; + ServerMethodOutputFile->keep(); ServerHeaderOutputFile->keep(); + LibrarySourceOutputFile->keep(); + LibraryHeaderOutputFile->keep(); return std::make_unique<SBConsumer>( Byproducts, CI.getSourceManager(), CI.getASTContext(), - std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile)); + std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), std::move(LibraryHeaderOutputFile)); } private: @@ -307,6 +351,28 @@ bool EmitAmalgamatedServerHeader(const std::vector<std::string> &Files) { return true; } +bool EmitAmalgamatedLibraryHeader(const std::vector<std::string> &Files) { + static constexpr llvm::StringLiteral AmalgamatedLibraryHeaderName = + "LLDBRPC.h"; + std::unique_ptr<llvm::ToolOutputFile> AmalgamatedLibraryHeader = + CreateOutputFile(GetLibraryOutputDirectory(), + AmalgamatedLibraryHeaderName); + if (!AmalgamatedLibraryHeader) + return false; + + AmalgamatedLibraryHeader->os() << "#ifndef LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#define LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#include \"SBLanguages.h\"\n"; + for (const auto &File : Files) { + llvm::StringRef Filename = llvm::sys::path::filename(File); + AmalgamatedLibraryHeader->os() << "#include \"" << Filename << "\"\n"; + } + + AmalgamatedLibraryHeader->os() << "#endif // LLDBRPC_H\n"; + AmalgamatedLibraryHeader->keep(); + return true; +} + bool EmitClassNamesFile(std::set<std::string> &ClassNames) { static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def"; std::unique_ptr<llvm::ToolOutputFile> ClassNamesFile = @@ -381,6 +447,19 @@ int main(int argc, const char *argv[]) { return 1; } + // Create the output directory if the user specified one does not exist. + if (!llvm::sys::fs::exists(OutputDir.getValue())) { + llvm::sys::fs::create_directory(OutputDir.getValue()); + } + + if (!llvm::sys::fs::exists(GetServerOutputDirectory())) { + llvm::sys::fs::create_directory(GetServerOutputDirectory()); + } + + if (!llvm::sys::fs::exists(GetLibraryOutputDirectory())) { + llvm::sys::fs::create_directory(GetLibraryOutputDirectory()); + } + CommonOptionsParser &OP = ExpectedParser.get(); auto PCHOpts = std::make_shared<PCHContainerOperations>(); PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>()); @@ -390,6 +469,10 @@ int main(int argc, const char *argv[]) { if (!EmitAmalgamatedServerHeader(OP.getSourcePathList())) { llvm::errs() << "Failed to create amalgamated server header\n"; + } + + if (!EmitAmalgamatedLibraryHeader(OP.getSourcePathList())) { + llvm::errs() << "Failed to create amalgamated library header\n"; return 1; } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits