brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.8 KiB · 7af6ee3 Raw
494 lines · cpp
1//===-- RPCCommon.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "RPCCommon.h"10 11#include "clang/AST/AST.h"12#include "clang/AST/Attr.h"13#include "clang/AST/DeclBase.h"14#include "clang/AST/Mangle.h"15#include "clang/Lex/Lexer.h"16 17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/ADT/StringMap.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/Support/raw_ostream.h"22 23#include <cstring>24 25using namespace clang;26 27// We intentionally do not generate some classes because they are currently28// inconvenient, they aren't really used by most consumers, or we're not sure29// why they exist.30static constexpr llvm::StringRef DisallowedClasses[] = {31    "SBCommunication", // This class is pretty much unused by consumers, so we32                       // skip it.33    "SBInputReader",   // This class is pretty much unused by consumers, so we34                       // skip it.35    "SBCommandPluginInterface", // This class uses virtual functions, and the SB36                                // API should not have those, so we skip this37                                // class.38    "SBCommand", // There's nothing too difficult about this one, but many of39                 // its methods take a SBCommandPluginInterface pointer so40                 // there's no reason to support this.41};42 43// NOTE: In lldb-rpc-gen, we use mangled names when we need to work with44// functions. We do this because we support many functions that have overloads,45// and mangled names have no ambiguity which makes it easier to keep track of.46// This is also possible since the LLDB SB API is stable.47 48// We intentionally avoid generating certain methods either because they are49// difficult to support correctly or they aren't really used much from C++.50// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED.51// Normally this macro defines to the deprecated annotation, but this52// functionality is removed in SBDefines.h when generating SWIG bindings which53// we use for testing. Because of this, there is no annotation for the tool to54// pick up on so this list will be used while we have this restriction in55// SBDefines.h.56static constexpr llvm::StringRef DisallowedMethods[] = {57    // The threading functionality in SBHostOS is deprecated and thus we do not58    // generate them. It would be ideal to add the annotations to the methods59    // and then support not generating deprecated methods. However, without60    // annotations the generator generates most things correctly. This one is61    // problematic because it returns a pointer to an "opaque" structure62    // (thread_t) that is not `void *`, so special casing it is more effort than63    // it's worth.64    "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE",65    "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE",66    "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE",67    "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE",68    "_ZN4lldb8SBHostOS13ThreadCreatedEPKc",69};70 71static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = {72    "SBHostOS",73    "SBReproducer",74};75 76static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = {77    "SBHostOS",78    "SBReproducer",79    "SBStream",80    "SBProgress",81};82 83static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = {84    "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm",85    "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh",86    "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh",87    "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym",88    "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm",89    "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm",90    "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim",91    "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm",92    "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym",93    "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm",94    "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm",95    "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim",96    "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm",97    "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm",98    "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm",99    "_ZN4lldb10SBDebugger13DispatchInputEPKvm",100    "_ZN4lldb6SBFile4ReadEPhmPm",101    "_ZN4lldb6SBFile5WriteEPKhmPm",102    "_ZNK4lldb10SBFileSpec7GetPathEPcm",103    "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm",104    "_ZN4lldb8SBModule10GetVersionEPjj",105    "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm",106    "_ZNK4lldb9SBProcess9GetSTDOUTEPcm",107    "_ZNK4lldb9SBProcess9GetSTDERREPcm",108    "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm",109    "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE",110    "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE",111    "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE",112    "_ZNK4lldb16SBStructuredData14GetStringValueEPcm",113    "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_"114    "14SBFileSpecListES6_",115    "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE",116    "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm",117    "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm",118    "_ZN4lldb8SBTarget15GetInstructionsEyPKvm",119    "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm",120    "_ZN4lldb8SBThread18GetStopDescriptionEPcm",121    // The below mangled names are used for dummy methods in shell tests122    // that test the emitters' output. If you're adding any new mangled names123    // from the actual SB API to this list please add them above.124    "_ZN4lldb33SBRPC_"125    "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm",126    "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm",127    "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm",128};129 130// These classes inherit from rpc::ObjectRef directly (as opposed to131// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI132// breaking, so we preserve that compatibility here.133//134// lldb-rpc-gen emits classes as LocalObjectRefs by default.135//136// FIXME: Does it matter which one it emits by default?137static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = {138    "SBAddress",139    "SBBreakpointName",140    "SBCommandInterpreter",141    "SBCommandReturnObject",142    "SBError",143    "SBExecutionContext",144    "SBExpressionOptions",145    "SBFileSpec",146    "SBFileSpecList",147    "SBFormat",148    "SBFunction",149    "SBHistoricalFrame",150    "SBHistoricalLineEntry",151    "SBHistoricalLineEntryList",152    "SBLineEntry",153    "SBStream",154    "SBStringList",155    "SBStructuredData",156    "SBSymbolContext",157    "SBSymbolContextList",158    "SBTypeMember",159    "SBTypeSummaryOptions",160    "SBValueList",161};162 163QualType lldb_rpc_gen::GetUnderlyingType(QualType T) {164  QualType UnderlyingType;165  if (T->isPointerType())166    UnderlyingType = T->getPointeeType();167  else if (T->isReferenceType())168    UnderlyingType = T.getNonReferenceType();169  else170    UnderlyingType = T;171 172  return UnderlyingType;173}174 175QualType lldb_rpc_gen::GetUnqualifiedUnderlyingType(QualType T) {176  return GetUnderlyingType(T).getUnqualifiedType();177}178 179std::string lldb_rpc_gen::GetMangledName(ASTContext &Context,180                                         CXXMethodDecl *MDecl) {181  std::string Mangled;182  llvm::raw_string_ostream MangledStream(Mangled);183 184  GlobalDecl GDecl;185  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl))186    GDecl = GlobalDecl(CtorDecl, Ctor_Complete);187  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(MDecl))188    GDecl = GlobalDecl(DtorDecl, Dtor_Deleting);189  else190    GDecl = GlobalDecl(MDecl);191 192  MangleContext *MC = Context.createMangleContext();193  MC->mangleName(GDecl, MangledStream);194  return Mangled;195}196 197bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) {198  auto CheckTypeForLLDBPrivate = [](const Type *Ty) {199    if (!Ty)200      return false;201    const auto *CXXRDecl = Ty->getAsCXXRecordDecl();202    if (!CXXRDecl)203      return false;204    const auto *NSDecl =205        llvm::dyn_cast<NamespaceDecl>(CXXRDecl->getDeclContext());206    if (!NSDecl)207      return false;208    return NSDecl->getName() == "lldb_private";209  };210 211  // First, get the underlying type (remove qualifications and strip off any212  // pointers/references). Then we'll need to desugar this type. This will213  // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll214  // actually see something like "std::shared_ptr<lldb_private::Debugger>".215  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);216  const Type *DesugaredType =217      UnqualifiedUnderlyingType->getUnqualifiedDesugaredType();218  assert(DesugaredType && "DesugaredType from a valid Type is nullptr!");219 220  // Check the type itself.221  if (CheckTypeForLLDBPrivate(DesugaredType))222    return true;223 224  // If that didn't work, it's possible that the type has a template argument225  // that is an lldb_private type.226  if (const auto *TemplateSDecl =227          llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(228              DesugaredType->getAsCXXRecordDecl())) {229    for (const TemplateArgument &TA :230         TemplateSDecl->getTemplateArgs().asArray()) {231      if (TA.getKind() != TemplateArgument::Type)232        continue;233      if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr()))234        return true;235    }236  }237  return false;238}239 240bool lldb_rpc_gen::TypeIsSBClass(QualType T) {241  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);242  const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();243  if (!CXXRDecl)244    return false; // SB Classes are always C++ classes245 246  return CXXRDecl->getName().starts_with("SB");247}248 249bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) {250  if (!T->isPointerType())251    return false;252 253  QualType UnderlyingType = T->getPointeeType();254  if (!UnderlyingType.isConstQualified())255    return false;256 257  // NOTE: We should be able to do `UnderlyingType->isCharType` but that will258  // return true for `const uint8_t *` since that is effectively an unsigned259  // char pointer. We currently do not support pointers other than `const char260  // *` and `const char **`.261 262  // NOTE: Checking that the underlying type is a signed integer works on Darwin263  // platforms, but Linux platforms expect that the underlying type is an264  // unsigned integer.265  return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) ||266         UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar) ||267         UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_U) ||268         UnderlyingType->isSpecificBuiltinType(BuiltinType::UChar);269}270 271bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) {272  if (!T->isPointerType())273    return false;274 275  return TypeIsConstCharPtr(T->getPointeeType());276}277 278bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) {279  QualType UUT = GetUnqualifiedUnderlyingType(T);280  const auto *CXXRDecl = UUT->getAsCXXRecordDecl();281  if (!CXXRDecl)282    return false;283 284  llvm::StringRef DeclName = CXXRDecl->getName();285  for (const llvm::StringRef DisallowedClass : DisallowedClasses)286    if (DeclName == DisallowedClass)287      return true;288  return false;289}290 291bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) {292  return T->isFunctionPointerType();293}294 295bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context,296                                      CXXMethodDecl *MDecl) {297  bool isDisallowed = false;298  std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl);299  if (llvm::is_contained(DisallowedMethods, MangledName))300    isDisallowed = true;301 302  if (MDecl->hasAttrs()) {303    for (auto *attr : MDecl->getAttrs()) {304      if (strcmp(attr->getAttrName()->getNameStart(), "deprecated") == 0)305        isDisallowed = true;306    }307  }308  return isDisallowed;309}310 311// NOTE: There's possibly a more clever way to do this, but we're keeping312// the string replacement way here. Here is why it is written this way:313// By the time we have already created a `Method` object, we have extracted the314// `QualifiedName` and the relevant QualTypes for parameters/return types, many315// of which contains "lldb::" in them. To change it in a way that would be316// friendly to liblldbrpc, we would need to have a way of replacing that317// namespace at the time of creating a Method, and only for liblldbrpc methods.318// IMO this would complicate Method more than what I'm doing here, and not319// necessarily for any more benefit.320// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do321// something similar to this. It also operates primarily on string replacement,322// but uses more sophisticated clang tooling to do so.323// For now, this will do what we need it to do.324std::string325lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) {326  const char *lldb_namespace = "lldb::";327  auto Pos = Name.find(lldb_namespace);328  while (Pos != std::string::npos) {329    constexpr size_t SizeOfLLDBNamespace = 6;330    Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc::");331    Pos = Name.find(lldb_namespace);332  }333  return Name;334}335 336std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) {337  const char *lldb_namespace = "lldb::";338  auto Pos = Name.find(lldb_namespace);339  if (Pos != std::string::npos) {340    constexpr size_t SizeOfLLDBNamespace = 6;341    Name = Name.substr(Pos + SizeOfLLDBNamespace);342  }343  return Name;344}345 346bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) {347  return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName);348}349 350bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) {351  return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName);352}353 354bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) {355  return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName);356}357 358std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) {359  assert(lldb_rpc_gen::TypeIsSBClass(T) &&360         "Cannot get SBClass name from non-SB class type!");361 362  QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);363  const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();364  assert(CXXRDecl && "SB class was not CXXRecordDecl!");365  if (!CXXRDecl)366    return std::string();367 368  return CXXRDecl->getName().str();369}370lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy,371                             ASTContext &Context)372    : Policy(Policy), Context(Context),373      QualifiedName(MDecl->getQualifiedNameAsString()),374      BaseName(MDecl->getNameAsString()),375      MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)),376      ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()),377      IsInstance(MDecl->isInstance()), IsCtor(isa<CXXConstructorDecl>(MDecl)),378      IsCopyAssign(MDecl->isCopyAssignmentOperator()),379      IsMoveAssign(MDecl->isMoveAssignmentOperator()),380      IsDtor(isa<CXXDestructorDecl>(MDecl)),381      IsConversionMethod(isa<CXXConversionDecl>(MDecl)) {382  uint8_t UnnamedArgIdx = 0;383  bool PrevParamWasPointer = false;384  for (const auto *ParamDecl : MDecl->parameters()) {385    Param param;386    if (ParamDecl->hasDefaultArg())387      param.DefaultValueText =388          Lexer::getSourceText(389              CharSourceRange::getTokenRange(390                  ParamDecl->getDefaultArg()->getSourceRange()),391              Context.getSourceManager(), Context.getLangOpts())392              .str();393 394    param.IsFollowedByLen = false;395    param.Name = ParamDecl->getNameAsString();396    // If the parameter has no name, we'll generate one397    if (param.Name.empty()) {398      param.Name = "arg" + std::to_string(UnnamedArgIdx);399      UnnamedArgIdx++;400    }401    param.Type = ParamDecl->getType();402 403    // FIXME: Instead of using this heuristic, the ideal thing would be to add404    // annotations to the SBAPI methods themselves. For now, we have a list of405    // methods that we know will need this.406    if (PrevParamWasPointer) {407      PrevParamWasPointer = false;408      const bool IsIntegerType = param.Type->isIntegerType() &&409                                 !param.Type->isBooleanType() &&410                                 !param.Type->isEnumeralType();411      if (IsIntegerType && llvm::is_contained(MethodsWithPointerPlusLen,412                                              llvm::StringRef(MangledName)))413        Params.back().IsFollowedByLen = true;414    }415 416    if (param.Type->isPointerType() &&417        !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) &&418        !param.Type->isFunctionPointerType())419      PrevParamWasPointer = true;420 421    if (param.Type->isFunctionPointerType())422      ContainsFunctionPointerParameter = true;423 424    Params.push_back(param);425  }426 427  if (IsInstance)428    ThisType = MDecl->getThisType();429 430  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl)) {431    IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit();432    IsCopyCtor = CtorDecl->isCopyConstructor();433    IsMoveCtor = CtorDecl->isMoveConstructor();434  } else if (const auto *ConversionDecl = dyn_cast<CXXConversionDecl>(MDecl))435    IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit();436}437 438// Adding a '<' allows us to use Methods in ordered containers.439// The ordering is on memory addresses.440bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const {441  return this < &rhs;442}443 444std::string445lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation,446                                              bool IncludeDefaultValue) const {447  assert((!IncludeDefaultValue || Generation == eLibrary) &&448         "Default values should only be emitted on the library side!");449 450  std::vector<std::string> ParamList;451 452  if (Generation == eLibrary && RequiresConnectionParameter())453    ParamList.push_back("const rpc::Connection &connection");454 455  for (const auto &Param : Params) {456    std::string ParamString;457    llvm::raw_string_ostream ParamStringStream(ParamString);458 459    if (Generation == eLibrary)460      ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(461          Param.Type.getAsString(Policy));462    else463      ParamStringStream << Param.Type.getAsString(Policy);464 465    ParamStringStream << " " << Param.Name;466    if (IncludeDefaultValue && Generation == eLibrary &&467        !Param.DefaultValueText.empty())468      ParamStringStream << " = "469                        << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(470                               Param.DefaultValueText);471 472    ParamList.push_back(ParamString);473  }474 475  return llvm::join(ParamList, ", ");476}477 478bool lldb_rpc_gen::Method::RequiresConnectionParameter() const {479  if (!IsCtor && IsInstance)480    return false;481  if (IsCopyCtor || IsMoveCtor)482    return false;483  for (const auto &Param : Params) {484    // We can re-use the connection from our parameter if possible.485    // Const-qualified parameters are input parameters and already486    // have a valid connection to provide to the current method.487    if (TypeIsSBClass(Param.Type) &&488        GetUnderlyingType(Param.Type).isConstQualified())489      return false;490  }491 492  return true;493}494