https://github.com/chelcassanova created 
https://github.com/llvm/llvm-project/pull/148981

In LLDB RPC, we need to keep track of methods that have a pointer parameter 
followed by a lengtgh parameter as these parameters need some exceptions when 
they're being emitted by lldb-rpc-gen. Previously, we used an exception list to 
keep track of every method that fell under this category using their mangled 
names. This method worked, but manually maintaining an exception list this way 
is unwieldly and can lead to hard-to-track errors for clients that use RPC and 
forget to add a method that they use to said list.

This commit changes this by using the Clang annotation attribute to annotate 
every method that uses a pointer plus length directly in the SB API, and checks 
that a given method has this attribute when determining if a method has a 
pointer plus length.

>From 9392a38149a580250cf590a2c05b205c14a0af17 Mon Sep 17 00:00:00 2001
From: Chelsea Cassanova <chelsea_cassan...@apple.com>
Date: Tue, 15 Jul 2025 14:29:36 -0500
Subject: [PATCH] [lldb][rpc] Use Clang attributes to keep track of pointer
 plus len

In LLDB RPC, we need to keep track of methods that have a pointer
parameter followed by a lengtgh parameter as these parameters need
some exceptions when they're being emitted by lldb-rpc-gen. Previously,
we used an exception list to keep track of every method that fell under
this category using their mangled names. This method worked, but
manually maintaining an exception list this way is unwieldly and can
lead to hard-to-track errors for clients that use RPC and forget to add
a method that they use to said list.

This commit changes this by using the Clang annotation attribute to
annotate every method that uses a pointer plus length directly in the SB
API, and checks that a given method has this attribute when determining
if a method has a pointer plus length.
---
 lldb/include/lldb/API/SBData.h                |  56 ++-
 lldb/include/lldb/API/SBDebugger.h            |   2 +
 lldb/include/lldb/API/SBFile.h                |   2 +
 lldb/include/lldb/API/SBFileSpec.h            |   2 +
 lldb/include/lldb/API/SBModule.h              |   1 +
 lldb/include/lldb/API/SBModuleSpec.h          |   1 +
 lldb/include/lldb/API/SBProcess.h             |   6 +
 lldb/include/lldb/API/SBStructuredData.h      |   1 +
 lldb/include/lldb/API/SBTarget.h              |  17 +-
 lldb/include/lldb/API/SBThread.h              |   1 +
 .../tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp | 469 ++++++++++++++++++
 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h  | 109 ++++
 12 files changed, 637 insertions(+), 30 deletions(-)
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h

diff --git a/lldb/include/lldb/API/SBData.h b/lldb/include/lldb/API/SBData.h
index 89a699f2f713a..1242ffdfc85b5 100644
--- a/lldb/include/lldb/API/SBData.h
+++ b/lldb/include/lldb/API/SBData.h
@@ -69,6 +69,7 @@ class LLDB_API SBData {
 
   const char *GetString(lldb::SBError &error, lldb::offset_t offset);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t ReadRawData(lldb::SBError &error, lldb::offset_t offset, void *buf,
                      size_t size);
 
@@ -80,9 +81,11 @@ class LLDB_API SBData {
   // DataExtractor, but having two SetData() signatures triggers a SWIG bug
   // where the typemap isn't applied before resolving the overload, and thus
   // the right function never gets called
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   void SetData(lldb::SBError &error, const void *buf, size_t size,
                lldb::ByteOrder endian, uint8_t addr_size);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   void SetDataWithOwnership(lldb::SBError &error, const void *buf, size_t size,
                             lldb::ByteOrder endian, uint8_t addr_size);
 
@@ -96,41 +99,46 @@ class LLDB_API SBData {
   // in the following CreateData*() and SetData*() prototypes, the two
   // parameters array and array_len should not be renamed or rearranged,
   // because doing so will break the SWIG typemap
-  static lldb::SBData CreateDataFromUInt64Array(lldb::ByteOrder endian,
-                                                uint32_t addr_byte_size,
-                                                uint64_t *array,
-                                                size_t array_len);
-
-  static lldb::SBData CreateDataFromUInt32Array(lldb::ByteOrder endian,
-                                                uint32_t addr_byte_size,
-                                                uint32_t *array,
-                                                size_t array_len);
-
-  static lldb::SBData CreateDataFromSInt64Array(lldb::ByteOrder endian,
-                                                uint32_t addr_byte_size,
-                                                int64_t *array,
-                                                size_t array_len);
-
-  static lldb::SBData CreateDataFromSInt32Array(lldb::ByteOrder endian,
-                                                uint32_t addr_byte_size,
-                                                int32_t *array,
-                                                size_t array_len);
-
-  static lldb::SBData CreateDataFromDoubleArray(lldb::ByteOrder endian,
-                                                uint32_t addr_byte_size,
-                                                double *array,
-                                                size_t array_len);
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  static lldb::SBData
+  CreateDataFromUInt64Array(lldb::ByteOrder endian, uint32_t addr_byte_size,
+                            uint64_t *array, size_t array_len);
+
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  static lldb::SBData
+  CreateDataFromUInt32Array(lldb::ByteOrder endian, uint32_t addr_byte_size,
+                            uint32_t *array, size_t array_len);
+
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  static lldb::SBData
+  CreateDataFromSInt64Array(lldb::ByteOrder endian, uint32_t addr_byte_size,
+                            int64_t *array, size_t array_len);
+
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  static lldb::SBData
+  CreateDataFromSInt32Array(lldb::ByteOrder endian, uint32_t addr_byte_size,
+                            int32_t *array, size_t array_len);
+
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  static lldb::SBData
+  CreateDataFromDoubleArray(lldb::ByteOrder endian, uint32_t addr_byte_size,
+                            double *array, size_t array_len);
 
   bool SetDataFromCString(const char *data);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   bool SetDataFromUInt64Array(uint64_t *array, size_t array_len);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   bool SetDataFromUInt32Array(uint32_t *array, size_t array_len);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   bool SetDataFromSInt64Array(int64_t *array, size_t array_len);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   bool SetDataFromSInt32Array(int32_t *array, size_t array_len);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   bool SetDataFromDoubleArray(double *array, size_t array_len);
 
 protected:
diff --git a/lldb/include/lldb/API/SBDebugger.h 
b/lldb/include/lldb/API/SBDebugger.h
index 192fbee9c0c6d..716d2bebb6bd6 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -323,6 +323,7 @@ class LLDB_API SBDebugger {
 
   bool GetUseSourceCache() const;
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   static bool GetDefaultArchitecture(char *arch_name, size_t arch_name_len);
 
   static bool SetDefaultArchitecture(const char *arch_name);
@@ -367,6 +368,7 @@ class LLDB_API SBDebugger {
   void DispatchInput(void *baton, const void *data, size_t data_len);
 #endif
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   void DispatchInput(const void *data, size_t data_len);
 
   void DispatchInputInterrupt();
diff --git a/lldb/include/lldb/API/SBFile.h b/lldb/include/lldb/API/SBFile.h
index ebdc5607b7942..842f0b86665a7 100644
--- a/lldb/include/lldb/API/SBFile.h
+++ b/lldb/include/lldb/API/SBFile.h
@@ -34,7 +34,9 @@ class LLDB_API SBFile {
 
   SBFile &operator=(const SBFile &rhs);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   SBError Read(uint8_t *buf, size_t num_bytes, size_t *OUTPUT);
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   SBError Write(const uint8_t *buf, size_t num_bytes, size_t *OUTPUT);
   SBError Flush();
   bool IsValid() const;
diff --git a/lldb/include/lldb/API/SBFileSpec.h 
b/lldb/include/lldb/API/SBFileSpec.h
index 36641843aabeb..1a25b0005e24c 100644
--- a/lldb/include/lldb/API/SBFileSpec.h
+++ b/lldb/include/lldb/API/SBFileSpec.h
@@ -51,8 +51,10 @@ class LLDB_API SBFileSpec {
 
   void SetDirectory(const char *directory);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   uint32_t GetPath(char *dst_path, size_t dst_len) const;
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   static int ResolvePath(const char *src_path, char *dst_path, size_t dst_len);
 
   bool GetDescription(lldb::SBStream &description) const;
diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 85332066ee687..7f39f7cc90368 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -274,6 +274,7 @@ class LLDB_API SBModule {
   ///     This function always returns the number of version numbers
   ///     that this object file has regardless of the number of
   ///     version numbers that were copied into \a versions.
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   uint32_t GetVersion(uint32_t *versions, uint32_t num_versions);
 
   /// Get accessor for the symbol file specification.
diff --git a/lldb/include/lldb/API/SBModuleSpec.h 
b/lldb/include/lldb/API/SBModuleSpec.h
index 8d1ecfe6e6f8b..fd48c585ed25f 100644
--- a/lldb/include/lldb/API/SBModuleSpec.h
+++ b/lldb/include/lldb/API/SBModuleSpec.h
@@ -71,6 +71,7 @@ class LLDB_API SBModuleSpec {
 
   void SetTriple(const char *triple);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   const uint8_t *GetUUIDBytes();
 
   size_t GetUUIDLength();
diff --git a/lldb/include/lldb/API/SBProcess.h 
b/lldb/include/lldb/API/SBProcess.h
index 882b8bd837131..a5784c893df7c 100644
--- a/lldb/include/lldb/API/SBProcess.h
+++ b/lldb/include/lldb/API/SBProcess.h
@@ -63,10 +63,13 @@ class LLDB_API SBProcess {
 
   size_t PutSTDIN(const char *src, size_t src_len);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t GetSTDOUT(char *dst, size_t dst_len) const;
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t GetSTDERR(char *dst, size_t dst_len) const;
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t GetAsyncProfileData(char *dst, size_t dst_len) const;
 
 #ifndef SWIG
@@ -197,11 +200,14 @@ class LLDB_API SBProcess {
   ///
   void ForceScriptedState(StateType new_state);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t ReadMemory(addr_t addr, void *buf, size_t size, lldb::SBError &error);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t WriteMemory(addr_t addr, const void *buf, size_t size,
                      lldb::SBError &error);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t ReadCStringFromMemory(addr_t addr, void *char_buf, size_t size,
                                lldb::SBError &error);
 
diff --git a/lldb/include/lldb/API/SBStructuredData.h 
b/lldb/include/lldb/API/SBStructuredData.h
index f96e169f236ed..4c972e2544a8a 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -104,6 +104,7 @@ class SBStructuredData {
   /// \return
   ///     Returns the byte size needed to completely write the string value at
   ///     \a dst in all cases.
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t GetStringValue(char *dst, size_t dst_len) const;
 
   /// Return the generic pointer if this data structure is a generic type.
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 2776a8f9010fe..7721db805d240 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -607,6 +607,7 @@ class LLDB_API SBTarget {
   ///
   /// \return
   ///     The amount of data read in host bytes.
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t ReadMemory(const SBAddress addr, void *buf, size_t size,
                     lldb::SBError &error);
 
@@ -688,12 +689,13 @@ class LLDB_API SBTarget {
       const SBFileSpecList &module_list,
       const SBFileSpecList &comp_unit_list);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   lldb::SBBreakpoint BreakpointCreateByNames(
       const char *symbol_name[], uint32_t num_names,
       uint32_t
           name_type_mask, // Logical OR one or more FunctionNameType enum bits
-      lldb::LanguageType symbol_language,
-      const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list);
+      lldb::LanguageType symbol_language, const SBFileSpecList &module_list,
+      const SBFileSpecList &comp_unit_list);
 
   lldb::SBBreakpoint BreakpointCreateByNames(
       const char *symbol_name[], uint32_t num_names,
@@ -900,24 +902,27 @@ class LLDB_API SBTarget {
                                            lldb::SBAddress end_addr,
                                            const char *flavor_string);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   lldb::SBInstructionList GetInstructions(lldb::SBAddress base_addr,
                                           const void *buf, size_t size);
 
   // The "WithFlavor" is necessary to keep SWIG from getting confused about
   // overloaded arguments when using the buf + size -> Python Object magic.
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   lldb::SBInstructionList GetInstructionsWithFlavor(lldb::SBAddress base_addr,
                                                     const char *flavor_string,
                                                     const void *buf,
                                                     size_t size);
 
 #ifndef SWIG
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   lldb::SBInstructionList GetInstructions(lldb::addr_t base_addr,
                                           const void *buf, size_t size);
-  lldb::SBInstructionList GetInstructionsWithFlavor(lldb::addr_t base_addr,
-                                                    const char *flavor_string,
-                                                    const void *buf,
-                                                    size_t size);
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
+  lldb::SBInstructionList
+  GetInstructionsWithFlavor(lldb::addr_t base_addr, const char *flavor_string,
+                            const void *buf, size_t size);
 #endif
 
   lldb::SBSymbolContextList FindSymbols(const char *name,
diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index f8ae627da5ace..317741ab38ff8 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -81,6 +81,7 @@ class LLDB_API SBThread {
   SBThreadCollection
   GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type);
 
+  [[clang::annotate("lldb-rpc-gen pointer plus len")]]
   size_t GetStopDescription(char *dst_or_null, size_t dst_len);
 
   SBValue GetStopReturnValue();
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
new file mode 100644
index 0000000000000..c396e10ed3e11
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
@@ -0,0 +1,469 @@
+//===-- RPCCommon.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 "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Lex/Lexer.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cstring>
+#include <iostream>
+
+using namespace clang;
+
+// We intentionally do not generate some classes because they are currently
+// inconvenient, they aren't really used by most consumers, or we're not sure
+// why they exist.
+static constexpr llvm::StringRef DisallowedClasses[] = {
+    "SBCommunication", // This class is pretty much unused by consumers, so we
+                       // skip it.
+    "SBInputReader",   // This class is pretty much unused by consumers, so we
+                       // skip it.
+    "SBCommandPluginInterface", // This class uses virtual functions, and the 
SB
+                                // API should not have those, so we skip this
+                                // class.
+    "SBCommand", // There's nothing too difficult about this one, but many of
+                 // its methods take a SBCommandPluginInterface pointer so
+                 // there's no reason to support this.
+};
+
+// NOTE: In lldb-rpc-gen, we use mangled names when we need to work with
+// functions. We do this because we support many functions that have overloads,
+// and mangled names have no ambiguity which makes it easier to keep track of.
+// This is also possible since the LLDB SB API is stable.
+
+// We intentionally avoid generating certain methods either because they are
+// difficult to support correctly or they aren't really used much from C++.
+// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED.
+// Normally this macro defines to the deprecated annotation, but this
+// functionality is removed in SBDefines.h when generating SWIG bindings which
+// we use for testing. Because of this, there is no annotation for the tool to
+// pick up on so this list will be used while we have this restriction in
+// SBDefines.h.
+static constexpr llvm::StringRef DisallowedMethods[] = {
+    // The threading functionality in SBHostOS is deprecated and thus we do not
+    // generate them. It would be ideal to add the annotations to the methods
+    // and then support not generating deprecated methods. However, without
+    // annotations the generator generates most things correctly. This one is
+    // problematic because it returns a pointer to an "opaque" structure
+    // (thread_t) that is not `void *`, so special casing it is more effort 
than
+    // it's worth.
+    "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE",
+    "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE",
+    "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE",
+    "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE",
+    "_ZN4lldb8SBHostOS13ThreadCreatedEPKc",
+};
+
+static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = {
+    "SBHostOS",
+    "SBReproducer",
+};
+
+static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = {
+    "SBHostOS",
+    "SBReproducer",
+    "SBStream",
+    "SBProgress",
+};
+
+// These classes inherit from rpc::ObjectRef directly (as opposed to
+// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI
+// breaking, so we preserve that compatibility here.
+//
+// lldb-rpc-gen emits classes as LocalObjectRefs by default.
+//
+// FIXME: Does it matter which one it emits by default?
+static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = {
+    "SBAddress",
+    "SBBreakpointName",
+    "SBCommandInterpreter",
+    "SBCommandReturnObject",
+    "SBError",
+    "SBExecutionContext",
+    "SBExpressionOptions",
+    "SBFileSpec",
+    "SBFileSpecList",
+    "SBFormat",
+    "SBFunction",
+    "SBHistoricalFrame",
+    "SBHistoricalLineEntry",
+    "SBHistoricalLineEntryList",
+    "SBLineEntry",
+    "SBStream",
+    "SBStringList",
+    "SBStructuredData",
+    "SBSymbolContext",
+    "SBSymbolContextList",
+    "SBTypeMember",
+    "SBTypeSummaryOptions",
+    "SBValueList",
+};
+
+QualType lldb_rpc_gen::GetUnderlyingType(QualType T) {
+  QualType UnderlyingType;
+  if (T->isPointerType())
+    UnderlyingType = T->getPointeeType();
+  else if (T->isReferenceType())
+    UnderlyingType = T.getNonReferenceType();
+  else
+    UnderlyingType = T;
+
+  return UnderlyingType;
+}
+
+QualType lldb_rpc_gen::GetUnqualifiedUnderlyingType(QualType T) {
+  return GetUnderlyingType(T).getUnqualifiedType();
+}
+
+std::string lldb_rpc_gen::GetMangledName(ASTContext &Context,
+                                         CXXMethodDecl *MDecl) {
+  std::string Mangled;
+  llvm::raw_string_ostream MangledStream(Mangled);
+
+  GlobalDecl GDecl;
+  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl))
+    GDecl = GlobalDecl(CtorDecl, Ctor_Complete);
+  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(MDecl))
+    GDecl = GlobalDecl(DtorDecl, Dtor_Deleting);
+  else
+    GDecl = GlobalDecl(MDecl);
+
+  MangleContext *MC = Context.createMangleContext();
+  MC->mangleName(GDecl, MangledStream);
+  return Mangled;
+}
+
+static auto CheckTypeForLLDBPrivate = [](const Type *Ty) {};
+bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) {
+  auto CheckTypeForLLDBPrivate = [](const Type *Ty) {
+    if (!Ty)
+      return false;
+    const auto *CXXRDecl = Ty->getAsCXXRecordDecl();
+    if (!CXXRDecl)
+      return false;
+    const auto *NSDecl =
+        llvm::dyn_cast<NamespaceDecl>(CXXRDecl->getDeclContext());
+    if (!NSDecl)
+      return false;
+    return NSDecl->getName() == "lldb_private";
+  };
+
+  // First, get the underlying type (remove qualifications and strip off any
+  // pointers/references). Then we'll need to desugar this type. This will
+  // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll
+  // actually see something like "std::shared_ptr<lldb_private::Debugger>".
+  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+  const Type *DesugaredType =
+      UnqualifiedUnderlyingType->getUnqualifiedDesugaredType();
+  assert(DesugaredType && "DesugaredType from a valid Type is nullptr!");
+
+  // Check the type itself.
+  if (CheckTypeForLLDBPrivate(DesugaredType))
+    return true;
+
+  // If that didn't work, it's possible that the type has a template argument
+  // that is an lldb_private type.
+  if (const auto *TemplateSDecl =
+          llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+              DesugaredType->getAsCXXRecordDecl())) {
+    for (const TemplateArgument &TA :
+         TemplateSDecl->getTemplateArgs().asArray()) {
+      if (TA.getKind() != TemplateArgument::Type)
+        continue;
+      if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr()))
+        return true;
+    }
+  }
+  return false;
+}
+
+bool lldb_rpc_gen::TypeIsSBClass(QualType T) {
+  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+  const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();
+  if (!CXXRDecl)
+    return false; // SB Classes are always C++ classes
+
+  return CXXRDecl->getName().starts_with("SB");
+}
+
+bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) {
+  if (!T->isPointerType())
+    return false;
+
+  QualType UnderlyingType = T->getPointeeType();
+  if (!UnderlyingType.isConstQualified())
+    return false;
+
+  // NOTE: We should be able to do `UnderlyingType->isCharType` but that will
+  // return true for `const uint8_t *` since that is effectively an unsigned
+  // char pointer. We currently do not support pointers other than `const char
+  // *` and `const char **`.
+  return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) ||
+         UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar);
+}
+
+bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) {
+  if (!T->isPointerType())
+    return false;
+
+  return TypeIsConstCharPtr(T->getPointeeType());
+}
+
+bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) {
+  QualType UUT = GetUnqualifiedUnderlyingType(T);
+  const auto *CXXRDecl = UUT->getAsCXXRecordDecl();
+  if (!CXXRDecl)
+    return false;
+
+  llvm::StringRef DeclName = CXXRDecl->getName();
+  for (const llvm::StringRef DisallowedClass : DisallowedClasses)
+    if (DeclName == DisallowedClass)
+      return true;
+  return false;
+}
+
+bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) {
+  return T->isFunctionPointerType();
+}
+
+bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context,
+                                      CXXMethodDecl *MDecl) {
+  bool isDisallowed = false;
+  std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl);
+  if (llvm::is_contained(DisallowedMethods, MangledName))
+    isDisallowed = true;
+
+  if (MDecl->hasAttrs()) {
+    for (auto *attr : MDecl->getAttrs()) {
+      if (strcmp(attr->getAttrName()->getNameStart(), "deprecated") == 0)
+        isDisallowed = true;
+    }
+  }
+  return isDisallowed;
+}
+
+bool lldb_rpc_gen::HasCallbackParameter(CXXMethodDecl *MDecl) {
+  bool HasCallbackParameter = false;
+  bool HasBatonParameter = false;
+  auto End = MDecl->parameters().end();
+  for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) {
+    if ((*Iter)->getType()->isFunctionPointerType())
+      HasCallbackParameter = true;
+    else if ((*Iter)->getType()->isVoidPointerType())
+      HasBatonParameter = true;
+  }
+
+  return HasCallbackParameter && HasBatonParameter;
+}
+
+// Methods that have a pointer parameter followed by a length param need to be
+// kept track of in RPC. Such methods are annotated in the main SB API using
+// this Clang attribute: [[clang::annotate("lldb-rpc-gen pointer plus len")]].
+// This method checks that a given method has this attribute.
+//
+// NOTE: In the event that we end up using the Clang annotate attribute for
+// other things in RPC, we'd have to check the actual text for the annotation
+// and not just check if the method has an annotation at all.
+bool lldb_rpc_gen::MethodContainsPointerPlusLen(CXXMethodDecl *MDecl) {
+  return MDecl->hasAttr<clang::AnnotateAttr>();
+}
+
+// NOTE: There's possibly a more clever way to do this, but we're keeping
+// the string replacement way here. Here is why it is written this way:
+// By the time we have already created a `Method` object, we have extracted the
+// `QualifiedName` and the relevant QualTypes for parameters/return types, many
+// of which contains "lldb::" in them. To change it in a way that would be
+// friendly to liblldbrpc, we would need to have a way of replacing that
+// namespace at the time of creating a Method, and only for liblldbrpc methods.
+// IMO this would complicate Method more than what I'm doing here, and not
+// necessarily for any more benefit.
+// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do
+// something similar to this. It also operates primarily on string replacement,
+// but uses more sophisticated clang tooling to do so.
+// For now, this will do what we need it to do.
+std::string
+lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) {
+  const char *lldb_namespace = "lldb::";
+  auto Pos = Name.find(lldb_namespace);
+  while (Pos != std::string::npos) {
+    constexpr size_t SizeOfLLDBNamespace = 6;
+    Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc::");
+    Pos = Name.find(lldb_namespace);
+  }
+  return Name;
+}
+
+std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) {
+  const char *lldb_namespace = "lldb::";
+  auto Pos = Name.find(lldb_namespace);
+  if (Pos != std::string::npos) {
+    constexpr size_t SizeOfLLDBNamespace = 6;
+    Name = Name.substr(Pos + SizeOfLLDBNamespace);
+  }
+  return Name;
+}
+
+bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) {
+  return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName);
+}
+
+bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) 
{
+  return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName);
+}
+
+bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) {
+  return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName);
+}
+
+std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) {
+  assert(lldb_rpc_gen::TypeIsSBClass(T) &&
+         "Cannot get SBClass name from non-SB class type!");
+
+  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+  const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();
+  assert(CXXRDecl && "SB class was not CXXRecordDecl!");
+  if (!CXXRDecl)
+    return std::string();
+
+  return CXXRDecl->getName().str();
+}
+lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy 
&Policy,
+                             ASTContext &Context)
+    : Policy(Policy), Context(Context),
+      QualifiedName(MDecl->getQualifiedNameAsString()),
+      BaseName(MDecl->getNameAsString()),
+      MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)),
+      ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()),
+      IsInstance(MDecl->isInstance()), IsCtor(isa<CXXConstructorDecl>(MDecl)),
+      IsCopyAssign(MDecl->isCopyAssignmentOperator()),
+      IsMoveAssign(MDecl->isMoveAssignmentOperator()),
+      IsDtor(isa<CXXDestructorDecl>(MDecl)),
+      IsConversionMethod(isa<CXXConversionDecl>(MDecl)) {
+  uint8_t UnnamedArgIdx = 0;
+  bool PrevParamWasPointer = false;
+  for (const auto *ParamDecl : MDecl->parameters()) {
+    Param param;
+    if (ParamDecl->hasDefaultArg())
+      param.DefaultValueText =
+          Lexer::getSourceText(
+              CharSourceRange::getTokenRange(
+                  ParamDecl->getDefaultArg()->getSourceRange()),
+              Context.getSourceManager(), Context.getLangOpts())
+              .str();
+
+    param.IsFollowedByLen = false;
+    param.Name = ParamDecl->getNameAsString();
+    // If the parameter has no name, we'll generate one
+    if (param.Name.empty()) {
+      param.Name = "arg" + std::to_string(UnnamedArgIdx);
+      UnnamedArgIdx++;
+    }
+    param.Type = ParamDecl->getType();
+
+    // FIXME: Instead of using this heuristic, the ideal thing would be to add
+    // annotations to the SBAPI methods themselves. For now, we have a list of
+    // methods that we know will need this.
+    if (PrevParamWasPointer) {
+      PrevParamWasPointer = false;
+      const bool IsIntegerType = param.Type->isIntegerType() &&
+                                 !param.Type->isBooleanType() &&
+                                 !param.Type->isEnumeralType();
+      if (IsIntegerType && lldb_rpc_gen::MethodContainsPointerPlusLen(MDecl)) {
+
+        Params.back().IsFollowedByLen = true;
+      }
+    }
+
+    if (param.Type->isPointerType() &&
+        !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) &&
+        !param.Type->isFunctionPointerType())
+      PrevParamWasPointer = true;
+
+    if (param.Type->isFunctionPointerType())
+      ContainsFunctionPointerParameter = true;
+
+    Params.push_back(param);
+  }
+
+  if (IsInstance)
+    ThisType = MDecl->getThisType();
+
+  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl)) {
+    IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit();
+    IsCopyCtor = CtorDecl->isCopyConstructor();
+    IsMoveCtor = CtorDecl->isMoveConstructor();
+  } else if (const auto *ConversionDecl = dyn_cast<CXXConversionDecl>(MDecl))
+    IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit();
+}
+
+// Adding a '<' allows us to use Methods in ordered containers.
+// The ordering is on memory addresses.
+bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const {
+  return this < &rhs;
+}
+
+std::string
+lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation,
+                                              bool IncludeDefaultValue) const {
+  assert((!IncludeDefaultValue || Generation == eLibrary) &&
+         "Default values should only be emitted on the library side!");
+
+  std::vector<std::string> ParamList;
+
+  if (Generation == eLibrary && RequiresConnectionParameter())
+    ParamList.push_back("const rpc::Connection &connection");
+
+  for (const auto &Param : Params) {
+    std::string ParamString;
+    llvm::raw_string_ostream ParamStringStream(ParamString);
+
+    if (Generation == eLibrary)
+      ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+          Param.Type.getAsString(Policy));
+    else
+      ParamStringStream << Param.Type.getAsString(Policy);
+
+    ParamStringStream << " " << Param.Name;
+    if (IncludeDefaultValue && Generation == eLibrary &&
+        !Param.DefaultValueText.empty())
+      ParamStringStream << " = "
+                        << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+                               Param.DefaultValueText);
+
+    ParamList.push_back(ParamString);
+  }
+
+  return llvm::join(ParamList, ", ");
+}
+
+bool lldb_rpc_gen::Method::RequiresConnectionParameter() const {
+  if (!IsCtor && IsInstance)
+    return false;
+  if (IsCopyCtor || IsMoveCtor)
+    return false;
+  for (const auto &Param : Params)
+    // We can re-use the connection from our parameter if possible.
+    // Const-qualified parameters are input parameters and already
+    // have a valid connection to provide to the current method.
+    if (TypeIsSBClass(Param.Type) &&
+        GetUnderlyingType(Param.Type).isConstQualified())
+      return false;
+
+  return true;
+}
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h
new file mode 100644
index 0000000000000..c4ea9c4540297
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h
@@ -0,0 +1,109 @@
+//===-- RPCCommon.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_RPCCOMMON_H
+#define LLDB_RPC_GEN_RPCCOMMON_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <string>
+
+using namespace clang;
+
+namespace lldb_rpc_gen {
+QualType GetUnderlyingType(QualType T);
+QualType GetUnqualifiedUnderlyingType(QualType T);
+std::string GetMangledName(ASTContext &Context, CXXMethodDecl *MDecl);
+
+bool TypeIsFromLLDBPrivate(QualType T);
+bool TypeIsSBClass(QualType T);
+bool TypeIsConstCharPtr(QualType T);
+bool TypeIsConstCharPtrPtr(QualType T);
+bool TypeIsDisallowedClass(QualType T);
+bool TypeIsCallbackFunctionPointer(QualType T);
+
+bool MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl);
+bool HasCallbackParameter(CXXMethodDecl *MDecl);
+bool MethodContainsPointerPlusLen(CXXMethodDecl *MDecl);
+
+std::string ReplaceLLDBNamespaceWithRPCNamespace(std::string Name);
+std::string StripLLDBNamespace(std::string Name);
+bool SBClassRequiresDefaultCtor(const std::string &ClassName);
+bool SBClassRequiresCopyCtorAssign(const std::string &ClassName);
+bool SBClassInheritsFromObjectRef(const std::string &ClassName);
+std::string GetSBClassNameFromType(QualType T);
+struct Param {
+  std::string Name;
+  QualType Type;
+  std::string DefaultValueText;
+  bool IsFollowedByLen;
+};
+
+enum GenerationKind : bool { eServer, eLibrary };
+
+struct Method {
+  enum Type { eOther, eConstructor, eDestructor };
+
+  Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy,
+         ASTContext &Context);
+
+  // Adding a '<' allows us to use Methods in ordered containers.
+  // The ordering is on memory addresses.
+  bool operator<(const lldb_rpc_gen::Method &rhs) const;
+  const PrintingPolicy &Policy;
+  const ASTContext &Context;
+  std::string QualifiedName;
+  std::string BaseName;
+  std::string MangledName;
+  QualType ReturnType;
+  QualType ThisType;
+  std::vector<Param> Params;
+  bool IsConst = false;
+  bool IsInstance = false;
+  bool IsCtor = false;
+  bool IsCopyCtor = false;
+  bool IsCopyAssign = false;
+  bool IsMoveCtor = false;
+  bool IsMoveAssign = false;
+  bool IsDtor = false;
+  bool IsConversionMethod = false;
+  bool IsExplicitCtorOrConversionMethod = false;
+  bool ContainsFunctionPointerParameter = false;
+
+  std::string CreateParamListAsString(GenerationKind Generation,
+                                      bool IncludeDefaultValue = false) const;
+
+  bool RequiresConnectionParameter() const;
+};
+
+std::string
+GetDefaultArgumentsForConstructor(std::string ClassName,
+                                  const lldb_rpc_gen::Method &method);
+
+class FileEmitter {
+protected:
+  FileEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile)
+      : OutputFile(std::move(OutputFile)), IndentLevel(0) {}
+  void EmitLine(const std::string &line) {
+    for (auto i = 0; i < IndentLevel; i++)
+      OutputFile->os() << "  ";
+
+    OutputFile->os() << line << "\n";
+  }
+
+  void EmitNewLine() { OutputFile->os() << "\n"; }
+
+  std::unique_ptr<llvm::ToolOutputFile> OutputFile;
+  uint8_t IndentLevel;
+};
+} // namespace lldb_rpc_gen
+#endif // LLDB_RPC_GEN_RPCCOMMON_H

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

Reply via email to