================ @@ -0,0 +1,504 @@ +//===-- 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/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" + +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", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// 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) { + CheckTypeForLLDBPrivate = { 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; + + // FIXME: 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) { + std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); + if (llvm::is_contained(DisallowedMethods, MangledName)) + return true; + + return MDecl->isDeprecatedInAnyTargetPlatform(); +} + +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; +} + +// FIXME: Find a better way to do this. 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) { + auto Pos = Name.find("lldb::"); + while (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc::"); + Pos = Name.find("lldb::"); + } + return Name; +} + +std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) { + auto Pos = Name.find("lldb::"); + if (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name = Name.substr(Pos + SizeOfLLDBNamespace); + } ---------------- chelcassanova wrote:
> is that intentional? I doubt it, the main reason I could think of is the function that replaces the lldb namespace with the RPC one might have to do it multiple times in the same string (like with function parameter names) while the one that strips the namespace usually has to do it once per string. https://github.com/llvm/llvm-project/pull/138031 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits