diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang')
40 files changed, 15975 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp new file mode 100644 index 00000000000..77bb9544ea4 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -0,0 +1,518 @@ +//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// +// +// 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 "ASTResultSynthesizer.h" + +#include "ClangPersistentVariables.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, + bool top_level, Target &target) + : m_ast_context(nullptr), m_passthrough(passthrough), + m_passthrough_sema(nullptr), m_target(target), m_sema(nullptr), + m_top_level(top_level) { + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough); +} + +ASTResultSynthesizer::~ASTResultSynthesizer() {} + +void ASTResultSynthesizer::Initialize(ASTContext &Context) { + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) { + if (log && log->GetVerbose()) { + if (named_decl->getIdentifier()) + LLDB_LOGF(log, "TransformTopLevelDecl(%s)", + named_decl->getIdentifier()->getNameStart()); + else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) + LLDB_LOGF(log, "TransformTopLevelDecl(%s)", + method_decl->getSelector().getAsString().c_str()); + else + LLDB_LOGF(log, "TransformTopLevelDecl(<complex>)"); + } + + if (m_top_level) { + RecordPersistentDecl(named_decl); + } + } + + if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) { + TransformTopLevelDecl(*decl_iterator); + } + } else if (!m_top_level) { + if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) { + if (m_ast_context && + !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { + RecordPersistentTypes(method_decl); + SynthesizeObjCMethodResult(method_decl); + } + } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) { + // When completing user input the body of the function may be a nullptr. + if (m_ast_context && function_decl->hasBody() && + !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { + RecordPersistentTypes(function_decl); + SynthesizeFunctionResult(function_decl); + } + } + } +} + +bool ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) { + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) { + Decl *decl = *decl_iterator; + + TransformTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +bool ASTResultSynthesizer::SynthesizeFunctionResult(FunctionDecl *FunDecl) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + FunctionDecl *function_decl = FunDecl; + + if (!function_decl) + return false; + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + LLDB_LOGF(log, "Untransformed function AST:\n%s", s.c_str()); + } + + Stmt *function_body = function_decl->getBody(); + CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body); + + bool ret = SynthesizeBodyResult(compound_stmt, function_decl); + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + LLDB_LOGF(log, "Transformed function AST:\n%s", s.c_str()); + } + + return ret; +} + +bool ASTResultSynthesizer::SynthesizeObjCMethodResult( + ObjCMethodDecl *MethodDecl) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + if (!MethodDecl) + return false; + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + LLDB_LOGF(log, "Untransformed method AST:\n%s", s.c_str()); + } + + Stmt *method_body = MethodDecl->getBody(); + + if (!method_body) + return false; + + CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body); + + bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl); + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + LLDB_LOGF(log, "Transformed method AST:\n%s", s.c_str()); + } + + return ret; +} + +bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body, + DeclContext *DC) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + ASTContext &Ctx(*m_ast_context); + + if (!Body) + return false; + + if (Body->body_empty()) + return false; + + Stmt **last_stmt_ptr = Body->body_end() - 1; + Stmt *last_stmt = *last_stmt_ptr; + + while (dyn_cast<NullStmt>(last_stmt)) { + if (last_stmt_ptr != Body->body_begin()) { + last_stmt_ptr--; + last_stmt = *last_stmt_ptr; + } else { + return false; + } + } + + Expr *last_expr = dyn_cast<Expr>(last_stmt); + + if (!last_expr) + // No auxiliary variable necessary; expression returns void + return true; + + // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off + // if that's the case. + + do { + ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr); + + if (!implicit_cast) + break; + + if (implicit_cast->getCastKind() != CK_LValueToRValue) + break; + + last_expr = implicit_cast->getSubExpr(); + } while (false); + + // is_lvalue is used to record whether the expression returns an assignable + // Lvalue or an Rvalue. This is relevant because they are handled + // differently. + // + // For Lvalues + // + // - In AST result synthesis (here!) the expression E is transformed into an + // initialization + // T *$__lldb_expr_result_ptr = &E. + // + // - In structure allocation, a pointer-sized slot is allocated in the + // struct that is to be + // passed into the expression. + // + // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are + // redirected at + // an entry in the struct ($__lldb_arg) passed into the expression. + // (Other persistent + // variables are treated similarly, having been materialized as + // references, but in those + // cases the value of the reference itself is never modified.) + // + // - During materialization, $0 (the result persistent variable) is ignored. + // + // - During dematerialization, $0 is marked up as a load address with value + // equal to the + // contents of the structure entry. + // + // For Rvalues + // + // - In AST result synthesis the expression E is transformed into an + // initialization + // static T $__lldb_expr_result = E. + // + // - In structure allocation, a pointer-sized slot is allocated in the + // struct that is to be + // passed into the expression. + // + // - In IR transformations, an instruction is inserted at the beginning of + // the function to + // dereference the pointer resident in the slot. Reads and writes to + // $__lldb_expr_result + // are redirected at that dereferenced version. Guard variables for the + // static variable + // are excised. + // + // - During materialization, $0 (the result persistent variable) is + // populated with the location + // of a newly-allocated area of memory. + // + // - During dematerialization, $0 is ignored. + + bool is_lvalue = last_expr->getValueKind() == VK_LValue && + last_expr->getObjectKind() == OK_Ordinary; + + QualType expr_qual_type = last_expr->getType(); + const clang::Type *expr_type = expr_qual_type.getTypePtr(); + + if (!expr_type) + return false; + + if (expr_type->isVoidType()) + return true; + + if (log) { + std::string s = expr_qual_type.getAsString(); + + LLDB_LOGF(log, "Last statement is an %s with type: %s", + (is_lvalue ? "lvalue" : "rvalue"), s.c_str()); + } + + clang::VarDecl *result_decl = nullptr; + + if (is_lvalue) { + IdentifierInfo *result_ptr_id; + + if (expr_type->isFunctionType()) + result_ptr_id = + &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should + // be treated like function + // pointers + else + result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr"); + + m_sema->RequireCompleteType(SourceLocation(), expr_qual_type, + clang::diag::err_incomplete_type); + + QualType ptr_qual_type; + + if (expr_qual_type->getAs<ObjCObjectType>() != nullptr) + ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type); + else + ptr_qual_type = Ctx.getPointerType(expr_qual_type); + + result_decl = + VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), + result_ptr_id, ptr_qual_type, nullptr, SC_Static); + + if (!result_decl) + return false; + + ExprResult address_of_expr = + m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); + if (address_of_expr.get()) + m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true); + else + return false; + } else { + IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); + + result_decl = + VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id, + expr_qual_type, nullptr, SC_Static); + + if (!result_decl) + return false; + + m_sema->AddInitializerToDecl(result_decl, last_expr, true); + } + + DC->addDecl(result_decl); + + /////////////////////////////// + // call AddInitializerToDecl + // + + // m_sema->AddInitializerToDecl(result_decl, last_expr); + + ///////////////////////////////// + // call ConvertDeclToDeclGroup + // + + Sema::DeclGroupPtrTy result_decl_group_ptr; + + result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl); + + //////////////////////// + // call ActOnDeclStmt + // + + StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt( + result_decl_group_ptr, SourceLocation(), SourceLocation())); + + //////////////////////////////////////////////// + // replace the old statement with the new one + // + + *last_stmt_ptr = static_cast<Stmt *>(result_initialization_stmt_result.get()); + + return true; +} + +void ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) { + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) { + typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator; + + for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()), + e = TypeDeclIterator(FunDeclCtx->decls_end()); + i != e; ++i) { + MaybeRecordPersistentType(*i); + } +} + +void ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) { + if (!D->getIdentifier()) + return; + + StringRef name = D->getName(); + + if (name.size() == 0 || name[0] != '$') + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + ConstString name_cs(name.str().c_str()); + + LLDB_LOGF(log, "Recording persistent type %s\n", name_cs.GetCString()); + + m_decls.push_back(D); +} + +void ASTResultSynthesizer::RecordPersistentDecl(NamedDecl *D) { + lldbassert(m_top_level); + + if (!D->getIdentifier()) + return; + + StringRef name = D->getName(); + + if (name.size() == 0) + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + ConstString name_cs(name.str().c_str()); + + LLDB_LOGF(log, "Recording persistent decl %s\n", name_cs.GetCString()); + + m_decls.push_back(D); +} + +void ASTResultSynthesizer::CommitPersistentDecls() { + auto *state = + m_target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC); + if (!state) + return; + + auto *persistent_vars = llvm::cast<ClangPersistentVariables>(state); + ClangASTContext *scratch_ctx = ClangASTContext::GetScratch(m_target); + + for (clang::NamedDecl *decl : m_decls) { + StringRef name = decl->getName(); + ConstString name_cs(name.str().c_str()); + + Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl( + &scratch_ctx->getASTContext(), decl); + + if (!D_scratch) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (log) { + std::string s; + llvm::raw_string_ostream ss(s); + decl->dump(ss); + ss.flush(); + + LLDB_LOGF(log, "Couldn't commit persistent decl: %s\n", s.c_str()); + } + + continue; + } + + if (NamedDecl *NamedDecl_scratch = dyn_cast<NamedDecl>(D_scratch)) + persistent_vars->RegisterPersistentDecl(name_cs, NamedDecl_scratch, + scratch_ctx); + } +} + +void ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) { + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) { + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) { + if (m_passthrough) + m_passthrough->HandleVTable(RD); +} + +void ASTResultSynthesizer::PrintStats() { + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void ASTResultSynthesizer::InitializeSema(Sema &S) { + m_sema = &S; + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void ASTResultSynthesizer::ForgetSema() { + m_sema = nullptr; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h new file mode 100644 index 00000000000..0b0f3b97705 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h @@ -0,0 +1,166 @@ +//===-- ASTResultSynthesizer.h ----------------------------------*- C++ -*-===// +// +// 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 liblldb_ASTResultSynthesizer_h_ +#define liblldb_ASTResultSynthesizer_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Target/Target.h" +#include "clang/Sema/SemaConsumer.h" + +namespace lldb_private { + +/// \class ASTResultSynthesizer ASTResultSynthesizer.h +/// "lldb/Expression/ASTResultSynthesizer.h" Adds a result variable +/// declaration to the ASTs for an expression. +/// +/// Users expect the expression "i + 3" to return a result, even if a result +/// variable wasn't specifically declared. To fulfil this requirement, LLDB +/// adds a result variable to the expression, transforming it to "int +/// $__lldb_expr_result = i + 3." The IR transformers ensure that the +/// resulting variable is mapped to the right piece of memory. +/// ASTResultSynthesizer's job is to add the variable and its initialization +/// to the ASTs for the expression, and it does so by acting as a SemaConsumer +/// for Clang. +class ASTResultSynthesizer : public clang::SemaConsumer { +public: + /// Constructor + /// + /// \param[in] passthrough + /// Since the ASTs must typically go through to the Clang code generator + /// in order to produce LLVM IR, this SemaConsumer must allow them to + /// pass to the next step in the chain after processing. Passthrough is + /// the next ASTConsumer, or NULL if none is required. + /// + /// \param[in] top_level + /// If true, register all top-level Decls and don't try to handle the + /// main function. + /// + /// \param[in] target + /// The target, which contains the persistent variable store and the + /// AST importer. + ASTResultSynthesizer(clang::ASTConsumer *passthrough, bool top_level, + Target &target); + + /// Destructor + ~ASTResultSynthesizer() override; + + /// Link this consumer with a particular AST context + /// + /// \param[in] Context + /// This AST context will be used for types and identifiers, and also + /// forwarded to the passthrough consumer, if one exists. + void Initialize(clang::ASTContext &Context) override; + + /// Examine a list of Decls to find the function $__lldb_expr and transform + /// its code + /// + /// \param[in] D + /// The list of Decls to search. These may contain LinkageSpecDecls, + /// which need to be searched recursively. That job falls to + /// TransformTopLevelDecl. + bool HandleTopLevelDecl(clang::DeclGroupRef D) override; + + /// Passthrough stub + void HandleTranslationUnit(clang::ASTContext &Ctx) override; + + /// Passthrough stub + void HandleTagDeclDefinition(clang::TagDecl *D) override; + + /// Passthrough stub + void CompleteTentativeDefinition(clang::VarDecl *D) override; + + /// Passthrough stub + void HandleVTable(clang::CXXRecordDecl *RD) override; + + /// Passthrough stub + void PrintStats() override; + + /// Set the Sema object to use when performing transforms, and pass it on + /// + /// \param[in] S + /// The Sema to use. Because Sema isn't externally visible, this class + /// casts it to an Action for actual use. + void InitializeSema(clang::Sema &S) override; + + /// Reset the Sema to NULL now that transformations are done + void ForgetSema() override; + + /// The parse has succeeded, so record its persistent decls + void CommitPersistentDecls(); + +private: + /// Hunt the given Decl for FunctionDecls named $__lldb_expr, recursing as + /// necessary through LinkageSpecDecls, and calling SynthesizeResult on + /// anything that was found + /// + /// \param[in] D + /// The Decl to hunt. + void TransformTopLevelDecl(clang::Decl *D); + + /// Process an Objective-C method and produce the result variable and + /// initialization + /// + /// \param[in] MethodDecl + /// The method to process. + bool SynthesizeObjCMethodResult(clang::ObjCMethodDecl *MethodDecl); + + /// Process a function and produce the result variable and initialization + /// + /// \param[in] FunDecl + /// The function to process. + bool SynthesizeFunctionResult(clang::FunctionDecl *FunDecl); + + /// Process a function body and produce the result variable and + /// initialization + /// + /// \param[in] Body + /// The body of the function. + /// + /// \param[in] DC + /// The DeclContext of the function, into which the result variable + /// is inserted. + bool SynthesizeBodyResult(clang::CompoundStmt *Body, clang::DeclContext *DC); + + /// Given a DeclContext for a function or method, find all types declared in + /// the context and record any persistent types found. + /// + /// \param[in] FunDeclCtx + /// The context for the function to process. + void RecordPersistentTypes(clang::DeclContext *FunDeclCtx); + + /// Given a TypeDecl, if it declares a type whose name starts with a dollar + /// sign, register it as a pointer type in the target's scratch AST context. + void MaybeRecordPersistentType(clang::TypeDecl *D); + + /// Given a NamedDecl, register it as a pointer type in the target's scratch + /// AST context. + void RecordPersistentDecl(clang::NamedDecl *D); + + clang::ASTContext + *m_ast_context; ///< The AST context to use for identifiers and types. + clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for + ///passthrough. NULL if it's a + ///SemaConsumer. + clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain, + ///for passthrough. NULL if it's an + ///ASTConsumer. + + std::vector<clang::NamedDecl *> m_decls; ///< Persistent declarations to + ///register assuming the expression + ///succeeds. + + Target &m_target; ///< The target, which contains the persistent variable + ///store and the + clang::Sema *m_sema; ///< The Sema to use. + bool m_top_level; +}; + +} // namespace lldb_private + +#endif // liblldb_ASTResultSynthesizer_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp new file mode 100644 index 00000000000..a164d48ae3e --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp @@ -0,0 +1,183 @@ +//===-- ASTStructExtractor.cpp ----------------------------------*- C++ -*-===// +// +// 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 "ASTStructExtractor.h" + +#include "lldb/Utility/Log.h" +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough, + const char *struct_name, + ClangFunctionCaller &function) + : m_ast_context(nullptr), m_passthrough(passthrough), + m_passthrough_sema(nullptr), m_sema(nullptr), m_function(function), + m_struct_name(struct_name) { + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough); +} + +ASTStructExtractor::~ASTStructExtractor() {} + +void ASTStructExtractor::Initialize(ASTContext &Context) { + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void ASTStructExtractor::ExtractFromFunctionDecl(FunctionDecl *F) { + if (!F->hasBody()) + return; + + Stmt *body_stmt = F->getBody(); + CompoundStmt *body_compound_stmt = dyn_cast<CompoundStmt>(body_stmt); + + if (!body_compound_stmt) + return; // do we have to handle this? + + RecordDecl *struct_decl = nullptr; + + StringRef desired_name(m_struct_name); + + for (CompoundStmt::const_body_iterator bi = body_compound_stmt->body_begin(), + be = body_compound_stmt->body_end(); + bi != be; ++bi) { + Stmt *curr_stmt = *bi; + DeclStmt *curr_decl_stmt = dyn_cast<DeclStmt>(curr_stmt); + if (!curr_decl_stmt) + continue; + DeclGroupRef decl_group = curr_decl_stmt->getDeclGroup(); + for (Decl *candidate_decl : decl_group) { + RecordDecl *candidate_record_decl = dyn_cast<RecordDecl>(candidate_decl); + if (!candidate_record_decl) + continue; + if (candidate_record_decl->getName() == desired_name) { + struct_decl = candidate_record_decl; + break; + } + } + if (struct_decl) + break; + } + + if (!struct_decl) + return; + + const ASTRecordLayout *struct_layout( + &m_ast_context->getASTRecordLayout(struct_decl)); + + if (!struct_layout) + return; + + m_function.m_struct_size = + struct_layout->getSize() + .getQuantity(); // TODO Store m_struct_size as CharUnits + m_function.m_return_offset = + struct_layout->getFieldOffset(struct_layout->getFieldCount() - 1) / 8; + m_function.m_return_size = + struct_layout->getDataSize().getQuantity() - m_function.m_return_offset; + + for (unsigned field_index = 0, num_fields = struct_layout->getFieldCount(); + field_index < num_fields; ++field_index) { + m_function.m_member_offsets.push_back( + struct_layout->getFieldOffset(field_index) / 8); + } + + m_function.m_struct_valid = true; +} + +void ASTStructExtractor::ExtractFromTopLevelDecl(Decl *D) { + LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D); + + if (linkage_spec_decl) { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) { + ExtractFromTopLevelDecl(*decl_iterator); + } + } + + FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D); + + if (m_ast_context && function_decl && + !m_function.m_wrapper_function_name.compare( + function_decl->getNameAsString())) { + ExtractFromFunctionDecl(function_decl); + } +} + +bool ASTStructExtractor::HandleTopLevelDecl(DeclGroupRef D) { + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) { + Decl *decl = *decl_iterator; + + ExtractFromTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +void ASTStructExtractor::HandleTranslationUnit(ASTContext &Ctx) { + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void ASTStructExtractor::HandleTagDeclDefinition(TagDecl *D) { + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void ASTStructExtractor::CompleteTentativeDefinition(VarDecl *D) { + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void ASTStructExtractor::HandleVTable(CXXRecordDecl *RD) { + if (m_passthrough) + m_passthrough->HandleVTable(RD); +} + +void ASTStructExtractor::PrintStats() { + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void ASTStructExtractor::InitializeSema(Sema &S) { + m_sema = &S; + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void ASTStructExtractor::ForgetSema() { + m_sema = nullptr; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h new file mode 100644 index 00000000000..078cf095975 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h @@ -0,0 +1,132 @@ +//===-- ASTStructExtractor.h ------------------------------------*- C++ -*-===// +// +// 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 liblldb_ASTStructExtractor_h_ +#define liblldb_ASTStructExtractor_h_ + +#include "ClangExpressionVariable.h" +#include "ClangFunctionCaller.h" + +#include "lldb/Core/ClangForward.h" +#include "clang/Sema/SemaConsumer.h" + +namespace lldb_private { + +/// \class ASTStructExtractor ASTStructExtractor.h +/// "lldb/Expression/ASTStructExtractor.h" Extracts and describes the argument +/// structure for a wrapped function. +/// +/// This pass integrates with ClangFunctionCaller, which calls functions with +/// custom sets of arguments. To avoid having to implement the full calling +/// convention for the target's architecture, ClangFunctionCaller writes a +/// simple wrapper function that takes a pointer to an argument structure that +/// contains room for the address of the function to be called, the values of +/// all its arguments, and room for the function's return value. +/// +/// The definition of this struct is itself in the body of the wrapper +/// function, so Clang does the structure layout itself. ASTStructExtractor +/// reads through the AST for the wrapper function and finds the struct. +class ASTStructExtractor : public clang::SemaConsumer { +public: + /// Constructor + /// + /// \param[in] passthrough + /// Since the ASTs must typically go through to the Clang code generator + /// in order to produce LLVM IR, this SemaConsumer must allow them to + /// pass to the next step in the chain after processing. Passthrough is + /// the next ASTConsumer, or NULL if none is required. + /// + /// \param[in] struct_name + /// The name of the structure to extract from the wrapper function. + /// + /// \param[in] function + /// The caller object whose members should be populated with information + /// about the argument struct. ClangFunctionCaller friends + /// ASTStructExtractor + /// for this purpose. + ASTStructExtractor(clang::ASTConsumer *passthrough, const char *struct_name, + ClangFunctionCaller &function); + + /// Destructor + ~ASTStructExtractor() override; + + /// Link this consumer with a particular AST context + /// + /// \param[in] Context + /// This AST context will be used for types and identifiers, and also + /// forwarded to the passthrough consumer, if one exists. + void Initialize(clang::ASTContext &Context) override; + + /// Examine a list of Decls to find the function $__lldb_expr and transform + /// its code + /// + /// \param[in] D + /// The list of Decls to search. These may contain LinkageSpecDecls, + /// which need to be searched recursively. That job falls to + /// TransformTopLevelDecl. + bool HandleTopLevelDecl(clang::DeclGroupRef D) override; + + /// Passthrough stub + void HandleTranslationUnit(clang::ASTContext &Ctx) override; + + /// Passthrough stub + void HandleTagDeclDefinition(clang::TagDecl *D) override; + + /// Passthrough stub + void CompleteTentativeDefinition(clang::VarDecl *D) override; + + /// Passthrough stub + void HandleVTable(clang::CXXRecordDecl *RD) override; + + /// Passthrough stub + void PrintStats() override; + + /// Set the Sema object to use when performing transforms, and pass it on + /// + /// \param[in] S + /// The Sema to use. Because Sema isn't externally visible, this class + /// casts it to an Action for actual use. + void InitializeSema(clang::Sema &S) override; + + /// Reset the Sema to NULL now that transformations are done + void ForgetSema() override; + +private: + /// Hunt the given FunctionDecl for the argument struct and place + /// information about it into m_function + /// + /// \param[in] F + /// The FunctionDecl to hunt. + void ExtractFromFunctionDecl(clang::FunctionDecl *F); + + /// Hunt the given Decl for FunctionDecls named the same as the wrapper + /// function name, recursing as necessary through LinkageSpecDecls, and + /// calling ExtractFromFunctionDecl on anything that was found + /// + /// \param[in] D + /// The Decl to hunt. + void ExtractFromTopLevelDecl(clang::Decl *D); + + clang::ASTContext + *m_ast_context; ///< The AST context to use for identifiers and types. + clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for + ///passthrough. NULL if it's a + ///SemaConsumer. + clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain, + ///for passthrough. NULL if it's an + ///ASTConsumer. + clang::Sema *m_sema; ///< The Sema to use. + + ClangFunctionCaller &m_function; ///< The function to populate with + ///information about the argument structure. + std::string m_struct_name; ///< The name of the structure to extract. +}; + +} // namespace lldb_private + +#endif // liblldb_ASTStructExtractor_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp new file mode 100644 index 00000000000..bbdf4e31c5a --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp @@ -0,0 +1,26 @@ +//===-- ASTUtils.cpp --------------------------------------------*- C++ -*-===// +// +// 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 "ASTUtils.h" + +lldb_private::ExternalASTSourceWrapper::~ExternalASTSourceWrapper() {} + +void lldb_private::ExternalASTSourceWrapper::PrintStats() { + m_Source->PrintStats(); +} + +lldb_private::ASTConsumerForwarder::~ASTConsumerForwarder() {} + +void lldb_private::ASTConsumerForwarder::PrintStats() { m_c->PrintStats(); } + +lldb_private::SemaSourceWithPriorities::~SemaSourceWithPriorities() {} + +void lldb_private::SemaSourceWithPriorities::PrintStats() { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->PrintStats(); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h new file mode 100644 index 00000000000..d429e8c3855 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h @@ -0,0 +1,579 @@ +//===-- ASTUtils.h ----------------------------------------------*- C++ -*-===// +// +// 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 liblldb_ASTUtils_h_ +#define liblldb_ASTUtils_h_ + +#include "clang/Sema/Lookup.h" +#include "clang/Sema/MultiplexExternalSemaSource.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaConsumer.h" + +namespace lldb_private { + +/// Wraps an ExternalASTSource into an ExternalSemaSource. Doesn't take +/// ownership of the provided source. +class ExternalASTSourceWrapper : public clang::ExternalSemaSource { + ExternalASTSource *m_Source; + +public: + ExternalASTSourceWrapper(ExternalASTSource *Source) : m_Source(Source) { + assert(m_Source && "Can't wrap nullptr ExternalASTSource"); + } + + ~ExternalASTSourceWrapper() override; + + clang::Decl *GetExternalDecl(uint32_t ID) override { + return m_Source->GetExternalDecl(ID); + } + + clang::Selector GetExternalSelector(uint32_t ID) override { + return m_Source->GetExternalSelector(ID); + } + + uint32_t GetNumExternalSelectors() override { + return m_Source->GetNumExternalSelectors(); + } + + clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override { + return m_Source->GetExternalDeclStmt(Offset); + } + + clang::CXXCtorInitializer ** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + return m_Source->GetExternalCXXCtorInitializers(Offset); + } + + clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + return m_Source->GetExternalCXXBaseSpecifiers(Offset); + } + + void updateOutOfDateIdentifier(clang::IdentifierInfo &II) override { + m_Source->updateOutOfDateIdentifier(II); + } + + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + return m_Source->FindExternalVisibleDeclsByName(DC, Name); + } + + void completeVisibleDeclsMap(const clang::DeclContext *DC) override { + m_Source->completeVisibleDeclsMap(DC); + } + + clang::Module *getModule(unsigned ID) override { + return m_Source->getModule(ID); + } + + llvm::Optional<ASTSourceDescriptor> + getSourceDescriptor(unsigned ID) override { + return m_Source->getSourceDescriptor(ID); + } + + ExtKind hasExternalDefinitions(const clang::Decl *D) override { + return m_Source->hasExternalDefinitions(D); + } + + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant, + llvm::SmallVectorImpl<clang::Decl *> &Result) override { + m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + } + + void + FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length, + llvm::SmallVectorImpl<clang::Decl *> &Decls) override { + m_Source->FindFileRegionDecls(File, Offset, Length, Decls); + } + + void CompleteRedeclChain(const clang::Decl *D) override { + m_Source->CompleteRedeclChain(D); + } + + void CompleteType(clang::TagDecl *Tag) override { + m_Source->CompleteType(Tag); + } + + void CompleteType(clang::ObjCInterfaceDecl *Class) override { + m_Source->CompleteType(Class); + } + + void ReadComments() override { m_Source->ReadComments(); } + + void StartedDeserializing() override { m_Source->StartedDeserializing(); } + + void FinishedDeserializing() override { m_Source->FinishedDeserializing(); } + + void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + m_Source->StartTranslationUnit(Consumer); + } + + void PrintStats() override; + + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &BaseOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &VirtualBaseOffsets) override { + return m_Source->layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets); + } +}; + +/// Wraps an ASTConsumer into an SemaConsumer. Doesn't take ownership of the +/// provided consumer. If the provided ASTConsumer is also a SemaConsumer, +/// the wrapper will also forward SemaConsumer functions. +class ASTConsumerForwarder : public clang::SemaConsumer { + clang::ASTConsumer *m_c; + clang::SemaConsumer *m_sc; + +public: + ASTConsumerForwarder(clang::ASTConsumer *c) : m_c(c) { + m_sc = llvm::dyn_cast<clang::SemaConsumer>(m_c); + } + + ~ASTConsumerForwarder() override; + + void Initialize(clang::ASTContext &Context) override { + m_c->Initialize(Context); + } + + bool HandleTopLevelDecl(clang::DeclGroupRef D) override { + return m_c->HandleTopLevelDecl(D); + } + + void HandleInlineFunctionDefinition(clang::FunctionDecl *D) override { + m_c->HandleInlineFunctionDefinition(D); + } + + void HandleInterestingDecl(clang::DeclGroupRef D) override { + m_c->HandleInterestingDecl(D); + } + + void HandleTranslationUnit(clang::ASTContext &Ctx) override { + m_c->HandleTranslationUnit(Ctx); + } + + void HandleTagDeclDefinition(clang::TagDecl *D) override { + m_c->HandleTagDeclDefinition(D); + } + + void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override { + m_c->HandleTagDeclRequiredDefinition(D); + } + + void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D) override { + m_c->HandleCXXImplicitFunctionInstantiation(D); + } + + void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef D) override { + m_c->HandleTopLevelDeclInObjCContainer(D); + } + + void HandleImplicitImportDecl(clang::ImportDecl *D) override { + m_c->HandleImplicitImportDecl(D); + } + + void CompleteTentativeDefinition(clang::VarDecl *D) override { + m_c->CompleteTentativeDefinition(D); + } + + void AssignInheritanceModel(clang::CXXRecordDecl *RD) override { + m_c->AssignInheritanceModel(RD); + } + + void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override { + m_c->HandleCXXStaticMemberVarInstantiation(D); + } + + void HandleVTable(clang::CXXRecordDecl *RD) override { + m_c->HandleVTable(RD); + } + + clang::ASTMutationListener *GetASTMutationListener() override { + return m_c->GetASTMutationListener(); + } + + clang::ASTDeserializationListener *GetASTDeserializationListener() override { + return m_c->GetASTDeserializationListener(); + } + + void PrintStats() override; + + void InitializeSema(clang::Sema &S) override { + if (m_sc) + m_sc->InitializeSema(S); + } + + /// Inform the semantic consumer that Sema is no longer available. + void ForgetSema() override { + if (m_sc) + m_sc->ForgetSema(); + } + + bool shouldSkipFunctionBody(clang::Decl *D) override { + return m_c->shouldSkipFunctionBody(D); + } +}; + +/// A ExternalSemaSource multiplexer that prioritizes its sources. +/// +/// This ExternalSemaSource will forward all requests to its attached sources. +/// However, unlike a normal multiplexer it will not forward a request to all +/// sources, but instead give priority to certain sources. If a source with a +/// higher priority can fulfill a request, all sources with a lower priority +/// will not receive the request. +/// +/// This class is mostly use to multiplex between sources of different +/// 'quality', e.g. a C++ modules and debug information. The C++ module will +/// provide more accurate replies to the requests, but might not be able to +/// answer all requests. The debug information will be used as a fallback then +/// to provide information that is not in the C++ module. +class SemaSourceWithPriorities : public clang::ExternalSemaSource { + +private: + /// The sources ordered in decreasing priority. + llvm::SmallVector<clang::ExternalSemaSource *, 2> Sources; + +public: + /// Construct a SemaSourceWithPriorities with a 'high quality' source that + /// has the higher priority and a 'low quality' source that will be used + /// as a fallback. + SemaSourceWithPriorities(clang::ExternalSemaSource &high_quality_source, + clang::ExternalSemaSource &low_quality_source) { + Sources.push_back(&high_quality_source); + Sources.push_back(&low_quality_source); + } + + ~SemaSourceWithPriorities() override; + + void addSource(clang::ExternalSemaSource &source) { + Sources.push_back(&source); + } + + //===--------------------------------------------------------------------===// + // ExternalASTSource. + //===--------------------------------------------------------------------===// + + clang::Decl *GetExternalDecl(uint32_t ID) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::Decl *Result = Sources[i]->GetExternalDecl(ID)) + return Result; + return nullptr; + } + + void CompleteRedeclChain(const clang::Decl *D) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->CompleteRedeclChain(D); + } + + clang::Selector GetExternalSelector(uint32_t ID) override { + clang::Selector Sel; + for (size_t i = 0; i < Sources.size(); ++i) { + Sel = Sources[i]->GetExternalSelector(ID); + if (!Sel.isNull()) + return Sel; + } + return Sel; + } + + uint32_t GetNumExternalSelectors() override { + for (size_t i = 0; i < Sources.size(); ++i) + if (uint32_t total = Sources[i]->GetNumExternalSelectors()) + return total; + return 0; + } + + clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::Stmt *Result = Sources[i]->GetExternalDeclStmt(Offset)) + return Result; + return nullptr; + } + + clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::CXXBaseSpecifier *R = + Sources[i]->GetExternalCXXBaseSpecifiers(Offset)) + return R; + return nullptr; + } + + clang::CXXCtorInitializer ** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + for (auto *S : Sources) + if (auto *R = S->GetExternalCXXCtorInitializers(Offset)) + return R; + return nullptr; + } + + ExtKind hasExternalDefinitions(const clang::Decl *D) override { + for (const auto &S : Sources) + if (auto EK = S->hasExternalDefinitions(D)) + if (EK != EK_ReplyHazy) + return EK; + return EK_ReplyHazy; + } + + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (Sources[i]->FindExternalVisibleDeclsByName(DC, Name)) + return true; + return false; + } + + void completeVisibleDeclsMap(const clang::DeclContext *DC) override { + // FIXME: Only one source should be able to complete the decls map. + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->completeVisibleDeclsMap(DC); + } + + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant, + llvm::SmallVectorImpl<clang::Decl *> &Result) override { + for (size_t i = 0; i < Sources.size(); ++i) { + Sources[i]->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + if (!Result.empty()) + return; + } + } + + void + FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length, + llvm::SmallVectorImpl<clang::Decl *> &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->FindFileRegionDecls(File, Offset, Length, Decls); + } + + void CompleteType(clang::TagDecl *Tag) override { + while (!Tag->isCompleteDefinition()) + for (size_t i = 0; i < Sources.size(); ++i) { + // FIXME: We are technically supposed to loop here too until + // Tag->isCompleteDefinition() is true, but if our low quality source + // is failing to complete the tag this code will deadlock. + Sources[i]->CompleteType(Tag); + if (Tag->isCompleteDefinition()) + break; + } + } + + void CompleteType(clang::ObjCInterfaceDecl *Class) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->CompleteType(Class); + } + + void ReadComments() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadComments(); + } + + void StartedDeserializing() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->StartedDeserializing(); + } + + void FinishedDeserializing() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->FinishedDeserializing(); + } + + void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->StartTranslationUnit(Consumer); + } + + void PrintStats() override; + + clang::Module *getModule(unsigned ID) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (auto M = Sources[i]->getModule(ID)) + return M; + return nullptr; + } + + bool DeclIsFromPCHWithObjectFile(const clang::Decl *D) override { + for (auto *S : Sources) + if (S->DeclIsFromPCHWithObjectFile(D)) + return true; + return false; + } + + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &BaseOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &VirtualBaseOffsets) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (Sources[i]->layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets)) + return true; + return false; + } + + void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override { + for (auto &Source : Sources) + Source->getMemoryBufferSizes(sizes); + } + + //===--------------------------------------------------------------------===// + // ExternalSemaSource. + //===--------------------------------------------------------------------===// + + void InitializeSema(clang::Sema &S) override { + for (auto &Source : Sources) + Source->InitializeSema(S); + } + + void ForgetSema() override { + for (auto &Source : Sources) + Source->ForgetSema(); + } + + void ReadMethodPool(clang::Selector Sel) override { + for (auto &Source : Sources) + Source->ReadMethodPool(Sel); + } + + void updateOutOfDateSelector(clang::Selector Sel) override { + for (auto &Source : Sources) + Source->updateOutOfDateSelector(Sel); + } + + void ReadKnownNamespaces( + llvm::SmallVectorImpl<clang::NamespaceDecl *> &Namespaces) override { + for (auto &Source : Sources) + Source->ReadKnownNamespaces(Namespaces); + } + + void ReadUndefinedButUsed( + llvm::MapVector<clang::NamedDecl *, clang::SourceLocation> &Undefined) + override { + for (auto &Source : Sources) + Source->ReadUndefinedButUsed(Undefined); + } + + void ReadMismatchingDeleteExpressions( + llvm::MapVector<clang::FieldDecl *, + llvm::SmallVector<std::pair<clang::SourceLocation, bool>, + 4>> &Exprs) override { + for (auto &Source : Sources) + Source->ReadMismatchingDeleteExpressions(Exprs); + } + + bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) override { + for (auto &Source : Sources) { + Source->LookupUnqualified(R, S); + if (!R.empty()) + break; + } + + return !R.empty(); + } + + void ReadTentativeDefinitions( + llvm::SmallVectorImpl<clang::VarDecl *> &Defs) override { + for (auto &Source : Sources) + Source->ReadTentativeDefinitions(Defs); + } + + void ReadUnusedFileScopedDecls( + llvm::SmallVectorImpl<const clang::DeclaratorDecl *> &Decls) override { + for (auto &Source : Sources) + Source->ReadUnusedFileScopedDecls(Decls); + } + + void ReadDelegatingConstructors( + llvm::SmallVectorImpl<clang::CXXConstructorDecl *> &Decls) override { + for (auto &Source : Sources) + Source->ReadDelegatingConstructors(Decls); + } + + void ReadExtVectorDecls( + llvm::SmallVectorImpl<clang::TypedefNameDecl *> &Decls) override { + for (auto &Source : Sources) + Source->ReadExtVectorDecls(Decls); + } + + void ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const clang::TypedefNameDecl *, 4> &Decls) override { + for (auto &Source : Sources) + Source->ReadUnusedLocalTypedefNameCandidates(Decls); + } + + void ReadReferencedSelectors( + llvm::SmallVectorImpl<std::pair<clang::Selector, clang::SourceLocation>> + &Sels) override { + for (auto &Source : Sources) + Source->ReadReferencedSelectors(Sels); + } + + void ReadWeakUndeclaredIdentifiers( + llvm::SmallVectorImpl<std::pair<clang::IdentifierInfo *, clang::WeakInfo>> + &WI) override { + for (auto &Source : Sources) + Source->ReadWeakUndeclaredIdentifiers(WI); + } + + void ReadUsedVTables( + llvm::SmallVectorImpl<clang::ExternalVTableUse> &VTables) override { + for (auto &Source : Sources) + Source->ReadUsedVTables(VTables); + } + + void ReadPendingInstantiations( + llvm::SmallVectorImpl< + std::pair<clang::ValueDecl *, clang::SourceLocation>> &Pending) + override { + for (auto &Source : Sources) + Source->ReadPendingInstantiations(Pending); + } + + void ReadLateParsedTemplates( + llvm::MapVector<const clang::FunctionDecl *, + std::unique_ptr<clang::LateParsedTemplate>> &LPTMap) + override { + for (auto &Source : Sources) + Source->ReadLateParsedTemplates(LPTMap); + } + + clang::TypoCorrection + CorrectTypo(const clang::DeclarationNameInfo &Typo, int LookupKind, + clang::Scope *S, clang::CXXScopeSpec *SS, + clang::CorrectionCandidateCallback &CCC, + clang::DeclContext *MemberContext, bool EnteringContext, + const clang::ObjCObjectPointerType *OPT) override { + for (auto &Source : Sources) { + if (clang::TypoCorrection C = + Source->CorrectTypo(Typo, LookupKind, S, SS, CCC, + MemberContext, EnteringContext, OPT)) + return C; + } + return clang::TypoCorrection(); + } + + bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc, + clang::QualType T) override { + for (auto &Source : Sources) { + if (Source->MaybeDiagnoseMissingCompleteType(Loc, T)) + return true; + } + return false; + } +}; + +} // namespace lldb_private +#endif // liblldb_ASTUtils_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt new file mode 100644 index 00000000000..e92d089cab1 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt @@ -0,0 +1,56 @@ +if(NOT LLDB_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lldb_library(lldbPluginExpressionParserClang PLUGIN + ASTResultSynthesizer.cpp + ASTStructExtractor.cpp + ASTUtils.cpp + ClangASTSource.cpp + ClangDeclVendor.cpp + ClangExpressionDeclMap.cpp + ClangExpressionParser.cpp + ClangExpressionSourceCode.cpp + ClangExpressionVariable.cpp + ClangFunctionCaller.cpp + ClangHost.cpp + ClangModulesDeclVendor.cpp + ClangPersistentVariables.cpp + ClangUserExpression.cpp + ClangUtilityFunction.cpp + CppModuleConfiguration.cpp + IRForTarget.cpp + IRDynamicChecks.cpp + + DEPENDS + ${tablegen_deps} + + LINK_LIBS + lldbCore + lldbExpression + lldbHost + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + lldbPluginCPlusPlusLanguage + lldbPluginCPPRuntime + CLANG_LIBS + clangAST + clangCodeGen + clangDriver + clangEdit + clangFrontend + clangLex + clangParse + clangRewrite + clangRewriteFrontend + clangSema + clangSerialization + LINK_COMPONENTS + Core + ExecutionEngine + ipo + MCJIT + Support + ) diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp new file mode 100644 index 00000000000..42927ab6cc8 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -0,0 +1,2018 @@ +//===-- ClangASTSource.cpp ---------------------------------------*- C++-*-===// +// +// 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 "ClangASTSource.h" + +#include "ClangDeclVendor.h" +#include "ClangModulesDeclVendor.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/CompilerDeclContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +#include <memory> +#include <vector> + +using namespace clang; +using namespace lldb_private; + +// Scoped class that will remove an active lexical decl from the set when it +// goes out of scope. +namespace { +class ScopedLexicalDeclEraser { +public: + ScopedLexicalDeclEraser(std::set<const clang::Decl *> &decls, + const clang::Decl *decl) + : m_active_lexical_decls(decls), m_decl(decl) {} + + ~ScopedLexicalDeclEraser() { m_active_lexical_decls.erase(m_decl); } + +private: + std::set<const clang::Decl *> &m_active_lexical_decls; + const clang::Decl *m_decl; +}; +} + +ClangASTSource::ClangASTSource(const lldb::TargetSP &target, + const lldb::ClangASTImporterSP &importer) + : m_import_in_progress(false), m_lookups_enabled(false), m_target(target), + m_ast_context(nullptr), m_active_lexical_decls(), m_active_lookups() { + m_ast_importer_sp = importer; +} + +void ClangASTSource::InstallASTContext(ClangASTContext &clang_ast_context) { + m_ast_context = &clang_ast_context.getASTContext(); + m_clang_ast_context = &clang_ast_context; + m_file_manager = &m_ast_context->getSourceManager().getFileManager(); + m_ast_importer_sp->InstallMapCompleter(m_ast_context, *this); +} + +ClangASTSource::~ClangASTSource() { + if (!m_ast_importer_sp) + return; + + m_ast_importer_sp->ForgetDestination(m_ast_context); + + if (!m_target) + return; + // We are in the process of destruction, don't create clang ast context on + // demand by passing false to + // Target::GetScratchClangASTContext(create_on_demand). + ClangASTContext *scratch_clang_ast_context = + ClangASTContext::GetScratch(*m_target, false); + + if (!scratch_clang_ast_context) + return; + + clang::ASTContext &scratch_ast_context = + scratch_clang_ast_context->getASTContext(); + + if (m_ast_context != &scratch_ast_context && m_ast_importer_sp) + m_ast_importer_sp->ForgetSource(&scratch_ast_context, m_ast_context); +} + +void ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) { + if (!m_ast_context) + return; + + m_ast_context->getTranslationUnitDecl()->setHasExternalVisibleStorage(); + m_ast_context->getTranslationUnitDecl()->setHasExternalLexicalStorage(); +} + +// The core lookup interface. +bool ClangASTSource::FindExternalVisibleDeclsByName( + const DeclContext *decl_ctx, DeclarationName clang_decl_name) { + if (!m_ast_context) { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + if (GetImportInProgress()) { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + std::string decl_name(clang_decl_name.getAsString()); + + // if (m_decl_map.DoingASTImport ()) + // return DeclContext::lookup_result(); + // + switch (clang_decl_name.getNameKind()) { + // Normal identifiers. + case DeclarationName::Identifier: { + clang::IdentifierInfo *identifier_info = + clang_decl_name.getAsIdentifierInfo(); + + if (!identifier_info || identifier_info->getBuiltinID() != 0) { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } break; + + // Operator names. + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + break; + + // Using directives found in this context. + // Tell Sema we didn't find any or we'll end up getting asked a *lot*. + case DeclarationName::CXXUsingDirective: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: { + llvm::SmallVector<NamedDecl *, 1> method_decls; + + NameSearchContext method_search_context(*this, method_decls, + clang_decl_name, decl_ctx); + + FindObjCMethodDecls(method_search_context); + + SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, method_decls); + return (method_decls.size() > 0); + } + // These aren't possible in the global context. + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + case DeclarationName::CXXDeductionGuideName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + if (!GetLookupsEnabled()) { + // Wait until we see a '$' at the start of a name before we start doing any + // lookups so we can avoid lookup up all of the builtin types. + if (!decl_name.empty() && decl_name[0] == '$') { + SetLookupsEnabled(true); + } else { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } + + ConstString const_decl_name(decl_name.c_str()); + + const char *uniqued_const_decl_name = const_decl_name.GetCString(); + if (m_active_lookups.find(uniqued_const_decl_name) != + m_active_lookups.end()) { + // We are currently looking up this name... + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + m_active_lookups.insert(uniqued_const_decl_name); + // static uint32_t g_depth = 0; + // ++g_depth; + // printf("[%5u] FindExternalVisibleDeclsByName() \"%s\"\n", g_depth, + // uniqued_const_decl_name); + llvm::SmallVector<NamedDecl *, 4> name_decls; + NameSearchContext name_search_context(*this, name_decls, clang_decl_name, + decl_ctx); + FindExternalVisibleDecls(name_search_context); + SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, name_decls); + // --g_depth; + m_active_lookups.erase(uniqued_const_decl_name); + return (name_decls.size() != 0); +} + +void ClangASTSource::CompleteType(TagDecl *tag_decl) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) { + LLDB_LOGF(log, + " CompleteTagDecl[%u] on (ASTContext*)%p Completing " + "(TagDecl*)%p named %s", + current_id, static_cast<void *>(m_ast_context), + static_cast<void *>(tag_decl), tag_decl->getName().str().c_str()); + + LLDB_LOG(log, " CTD[%u] Before:\n{0}", current_id, + ClangUtil::DumpDecl(tag_decl)); + } + + auto iter = m_active_lexical_decls.find(tag_decl); + if (iter != m_active_lexical_decls.end()) + return; + m_active_lexical_decls.insert(tag_decl); + ScopedLexicalDeclEraser eraser(m_active_lexical_decls, tag_decl); + + if (!m_ast_importer_sp) { + return; + } + + if (!m_ast_importer_sp->CompleteTagDecl(tag_decl)) { + // We couldn't complete the type. Maybe there's a definition somewhere + // else that can be completed. + + LLDB_LOGF(log, + " CTD[%u] Type could not be completed in the module in " + "which it was first found.", + current_id); + + bool found = false; + + DeclContext *decl_ctx = tag_decl->getDeclContext(); + + if (const NamespaceDecl *namespace_context = + dyn_cast<NamespaceDecl>(decl_ctx)) { + ClangASTImporter::NamespaceMapSP namespace_map = + m_ast_importer_sp->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + LLDB_LOGF(log, " CTD[%u] Inspecting namespace map %p (%d entries)", + current_id, static_cast<void *>(namespace_map.get()), + static_cast<int>(namespace_map->size())); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), + e = namespace_map->end(); + i != e && !found; ++i) { + LLDB_LOGF(log, " CTD[%u] Searching namespace %s in module %s", + current_id, i->second.GetName().AsCString(), + i->first->GetFileSpec().GetFilename().GetCString()); + + TypeList types; + + ConstString name(tag_decl->getName().str().c_str()); + + i->first->FindTypesInNamespace(name, &i->second, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + CompilerType clang_type(type->GetFullCompilerType()); + + if (!ClangUtil::IsClangType(clang_type)) + continue; + + const TagType *tag_type = + ClangUtil::GetQualType(clang_type)->getAs<TagType>(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = + const_cast<TagDecl *>(tag_type->getDecl()); + + if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl, + candidate_tag_decl)) + found = true; + } + } + } else { + TypeList types; + + ConstString name(tag_decl->getName().str().c_str()); + + const ModuleList &module_list = m_target->GetImages(); + + bool exact_match = false; + llvm::DenseSet<SymbolFile *> searched_symbol_files; + module_list.FindTypes(nullptr, name, exact_match, UINT32_MAX, + searched_symbol_files, types); + + for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + CompilerType clang_type(type->GetFullCompilerType()); + + if (!ClangUtil::IsClangType(clang_type)) + continue; + + const TagType *tag_type = + ClangUtil::GetQualType(clang_type)->getAs<TagType>(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = + const_cast<TagDecl *>(tag_type->getDecl()); + + // We have found a type by basename and we need to make sure the decl + // contexts are the same before we can try to complete this type with + // another + if (!ClangASTContext::DeclsAreEquivalent(tag_decl, candidate_tag_decl)) + continue; + + if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl, + candidate_tag_decl)) + found = true; + } + } + } + + LLDB_LOG(log, " [CTD] After:\n{0}", ClangUtil::DumpDecl(tag_decl)); +} + +void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOGF(log, + " [CompleteObjCInterfaceDecl] on (ASTContext*)%p Completing " + "an ObjCInterfaceDecl named %s", + static_cast<void *>(m_ast_context), + interface_decl->getName().str().c_str()); + LLDB_LOG(log, " [COID] Before:\n{0}", + ClangUtil::DumpDecl(interface_decl)); + + if (!m_ast_importer_sp) { + lldbassert(0 && "No mechanism for completing a type!"); + return; + } + + ClangASTImporter::DeclOrigin original = m_ast_importer_sp->GetDeclOrigin(interface_decl); + + if (original.Valid()) { + if (ObjCInterfaceDecl *original_iface_decl = + dyn_cast<ObjCInterfaceDecl>(original.decl)) { + ObjCInterfaceDecl *complete_iface_decl = + GetCompleteObjCInterface(original_iface_decl); + + if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) { + m_ast_importer_sp->SetDeclOrigin(interface_decl, complete_iface_decl); + } + } + } + + m_ast_importer_sp->CompleteObjCInterfaceDecl(interface_decl); + + if (interface_decl->getSuperClass() && + interface_decl->getSuperClass() != interface_decl) + CompleteType(interface_decl->getSuperClass()); + + if (log) { + LLDB_LOGF(log, " [COID] After:"); + LLDB_LOG(log, " [COID] {0}", ClangUtil::DumpDecl(interface_decl)); + } +} + +clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface( + const clang::ObjCInterfaceDecl *interface_decl) { + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return nullptr; + + ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process)); + + if (!language_runtime) + return nullptr; + + ConstString class_name(interface_decl->getNameAsString().c_str()); + + lldb::TypeSP complete_type_sp( + language_runtime->LookupInCompleteClassCache(class_name)); + + if (!complete_type_sp) + return nullptr; + + TypeFromUser complete_type = + TypeFromUser(complete_type_sp->GetFullCompilerType()); + lldb::opaque_compiler_type_t complete_opaque_type = + complete_type.GetOpaqueQualType(); + + if (!complete_opaque_type) + return nullptr; + + const clang::Type *complete_clang_type = + QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr(); + const ObjCInterfaceType *complete_interface_type = + dyn_cast<ObjCInterfaceType>(complete_clang_type); + + if (!complete_interface_type) + return nullptr; + + ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl()); + + return complete_iface_decl; +} + +void ClangASTSource::FindExternalLexicalDecls( + const DeclContext *decl_context, + llvm::function_ref<bool(Decl::Kind)> predicate, + llvm::SmallVectorImpl<Decl *> &decls) { + + if (!m_ast_importer_sp) + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + const Decl *context_decl = dyn_cast<Decl>(decl_context); + + if (!context_decl) + return; + + auto iter = m_active_lexical_decls.find(context_decl); + if (iter != m_active_lexical_decls.end()) + return; + m_active_lexical_decls.insert(context_decl); + ScopedLexicalDeclEraser eraser(m_active_lexical_decls, context_decl); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) { + if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context_decl)) + LLDB_LOGF( + log, + "FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p", + current_id, static_cast<void *>(m_ast_context), + context_named_decl->getNameAsString().c_str(), + context_decl->getDeclKindName(), + static_cast<const void *>(context_decl)); + else if (context_decl) + LLDB_LOGF( + log, "FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p", + current_id, static_cast<void *>(m_ast_context), + context_decl->getDeclKindName(), + static_cast<const void *>(context_decl)); + else + LLDB_LOGF( + log, + "FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context", + current_id, static_cast<const void *>(m_ast_context)); + } + + ClangASTImporter::DeclOrigin original = m_ast_importer_sp->GetDeclOrigin(context_decl); + + if (!original.Valid()) + return; + + LLDB_LOG( + log, " FELD[{0}] Original decl (ASTContext*){1:x} (Decl*){2:x}:\n{3}", + current_id, static_cast<void *>(original.ctx), + static_cast<void *>(original.decl), ClangUtil::DumpDecl(original.decl)); + + if (ObjCInterfaceDecl *original_iface_decl = + dyn_cast<ObjCInterfaceDecl>(original.decl)) { + ObjCInterfaceDecl *complete_iface_decl = + GetCompleteObjCInterface(original_iface_decl); + + if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) { + original.decl = complete_iface_decl; + original.ctx = &complete_iface_decl->getASTContext(); + + m_ast_importer_sp->SetDeclOrigin(context_decl, complete_iface_decl); + } + } + + if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original.decl)) { + ExternalASTSource *external_source = original.ctx->getExternalSource(); + + if (external_source) + external_source->CompleteType(original_tag_decl); + } + + const DeclContext *original_decl_context = + dyn_cast<DeclContext>(original.decl); + + if (!original_decl_context) + return; + + // Indicates whether we skipped any Decls of the original DeclContext. + bool SkippedDecls = false; + for (TagDecl::decl_iterator iter = original_decl_context->decls_begin(); + iter != original_decl_context->decls_end(); ++iter) { + Decl *decl = *iter; + + // The predicate function returns true if the passed declaration kind is + // the one we are looking for. + // See clang::ExternalASTSource::FindExternalLexicalDecls() + if (predicate(decl->getKind())) { + if (log) { + std::string ast_dump = ClangUtil::DumpDecl(decl); + if (const NamedDecl *context_named_decl = + dyn_cast<NamedDecl>(context_decl)) + LLDB_LOGF(log, " FELD[%d] Adding [to %sDecl %s] lexical %sDecl %s", + current_id, context_named_decl->getDeclKindName(), + context_named_decl->getNameAsString().c_str(), + decl->getDeclKindName(), ast_dump.c_str()); + else + LLDB_LOGF(log, " FELD[%d] Adding lexical %sDecl %s", current_id, + decl->getDeclKindName(), ast_dump.c_str()); + } + + Decl *copied_decl = CopyDecl(decl); + + if (!copied_decl) + continue; + + if (FieldDecl *copied_field = dyn_cast<FieldDecl>(copied_decl)) { + QualType copied_field_type = copied_field->getType(); + + m_ast_importer_sp->RequireCompleteType(copied_field_type); + } + } else { + SkippedDecls = true; + } + } + + // CopyDecl may build a lookup table which may set up ExternalLexicalStorage + // to false. However, since we skipped some of the external Decls we must + // set it back! + if (SkippedDecls) { + decl_context->setHasExternalLexicalStorage(true); + // This sets HasLazyExternalLexicalLookups to true. By setting this bit we + // ensure that the lookup table is rebuilt, which means the external source + // is consulted again when a clang::DeclContext::lookup is called. + const_cast<DeclContext *>(decl_context)->setMustBuildLookupTable(); + } + + return; +} + +void ClangASTSource::FindExternalVisibleDecls(NameSearchContext &context) { + assert(m_ast_context); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) { + if (!context.m_decl_context) + LLDB_LOGF(log, + "ClangASTSource::FindExternalVisibleDecls[%u] on " + "(ASTContext*)%p for '%s' in a NULL DeclContext", + current_id, static_cast<void *>(m_ast_context), + name.GetCString()); + else if (const NamedDecl *context_named_decl = + dyn_cast<NamedDecl>(context.m_decl_context)) + LLDB_LOGF(log, + "ClangASTSource::FindExternalVisibleDecls[%u] on " + "(ASTContext*)%p for '%s' in '%s'", + current_id, static_cast<void *>(m_ast_context), + name.GetCString(), + context_named_decl->getNameAsString().c_str()); + else + LLDB_LOGF(log, + "ClangASTSource::FindExternalVisibleDecls[%u] on " + "(ASTContext*)%p for '%s' in a '%s'", + current_id, static_cast<void *>(m_ast_context), + name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + context.m_namespace_map = std::make_shared<ClangASTImporter::NamespaceMap>(); + + if (const NamespaceDecl *namespace_context = + dyn_cast<NamespaceDecl>(context.m_decl_context)) { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer_sp ? + m_ast_importer_sp->GetNamespaceMap(namespace_context) : nullptr; + + if (log && log->GetVerbose()) + LLDB_LOGF(log, " CAS::FEVD[%u] Inspecting namespace map %p (%d entries)", + current_id, static_cast<void *>(namespace_map.get()), + static_cast<int>(namespace_map->size())); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), + e = namespace_map->end(); + i != e; ++i) { + LLDB_LOGF(log, " CAS::FEVD[%u] Searching namespace %s in module %s", + current_id, i->second.GetName().AsCString(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, i->first, i->second, current_id); + } + } else if (isa<ObjCInterfaceDecl>(context.m_decl_context)) { + FindObjCPropertyAndIvarDecls(context); + } else if (!isa<TranslationUnitDecl>(context.m_decl_context)) { + // we shouldn't be getting FindExternalVisibleDecls calls for these + return; + } else { + CompilerDeclContext namespace_decl; + + LLDB_LOGF(log, " CAS::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl, + current_id); + } + + if (!context.m_namespace_map->empty()) { + if (log && log->GetVerbose()) + LLDB_LOGF(log, + " CAS::FEVD[%u] Registering namespace map %p (%d entries)", + current_id, static_cast<void *>(context.m_namespace_map.get()), + static_cast<int>(context.m_namespace_map->size())); + + NamespaceDecl *clang_namespace_decl = + AddNamespace(context, context.m_namespace_map); + + if (clang_namespace_decl) + clang_namespace_decl->setHasExternalVisibleStorage(); + } +} + +clang::Sema *ClangASTSource::getSema() { + return m_clang_ast_context->getSema(); +} + +bool ClangASTSource::IgnoreName(const ConstString name, + bool ignore_all_dollar_names) { + static const ConstString id_name("id"); + static const ConstString Class_name("Class"); + + if (m_ast_context->getLangOpts().ObjC) + if (name == id_name || name == Class_name) + return true; + + StringRef name_string_ref = name.GetStringRef(); + + // The ClangASTSource is not responsible for finding $-names. + return name_string_ref.empty() || + (ignore_all_dollar_names && name_string_ref.startswith("$")) || + name_string_ref.startswith("_$"); +} + +void ClangASTSource::FindExternalVisibleDecls( + NameSearchContext &context, lldb::ModuleSP module_sp, + CompilerDeclContext &namespace_decl, unsigned int current_id) { + assert(m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + SymbolContextList sc_list; + + const ConstString name(context.m_decl_name.getAsString().c_str()); + if (IgnoreName(name, true)) + return; + + if (!m_target) + return; + + if (module_sp && namespace_decl) { + CompilerDeclContext found_namespace_decl; + + if (SymbolFile *symbol_file = module_sp->GetSymbolFile()) { + found_namespace_decl = symbol_file->FindNamespace(name, &namespace_decl); + + if (found_namespace_decl) { + context.m_namespace_map->push_back( + std::pair<lldb::ModuleSP, CompilerDeclContext>( + module_sp, found_namespace_decl)); + + LLDB_LOGF(log, " CAS::FEVD[%u] Found namespace %s in module %s", + current_id, name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + } else { + const ModuleList &target_images = m_target->GetImages(); + std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex()); + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + CompilerDeclContext found_namespace_decl; + + SymbolFile *symbol_file = image->GetSymbolFile(); + + if (!symbol_file) + continue; + + found_namespace_decl = symbol_file->FindNamespace(name, &namespace_decl); + + if (found_namespace_decl) { + context.m_namespace_map->push_back( + std::pair<lldb::ModuleSP, CompilerDeclContext>( + image, found_namespace_decl)); + + LLDB_LOGF(log, " CAS::FEVD[%u] Found namespace %s in module %s", + current_id, name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } + } + + do { + if (context.m_found.type) + break; + + TypeList types; + const bool exact_match = true; + llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files; + if (module_sp && namespace_decl) + module_sp->FindTypesInNamespace(name, &namespace_decl, 1, types); + else { + m_target->GetImages().FindTypes(module_sp.get(), name, exact_match, 1, + searched_symbol_files, types); + } + + if (size_t num_types = types.GetSize()) { + for (size_t ti = 0; ti < num_types; ++ti) { + lldb::TypeSP type_sp = types.GetTypeAtIndex(ti); + + if (log) { + const char *name_string = type_sp->GetName().GetCString(); + + LLDB_LOGF(log, " CAS::FEVD[%u] Matching type found for \"%s\": %s", + current_id, name.GetCString(), + (name_string ? name_string : "<anonymous>")); + } + + CompilerType full_type = type_sp->GetFullCompilerType(); + + CompilerType copied_clang_type(GuardedCopyType(full_type)); + + if (!copied_clang_type) { + LLDB_LOGF(log, " CAS::FEVD[%u] - Couldn't export a type", + current_id); + + continue; + } + + context.AddTypeDecl(copied_clang_type); + + context.m_found.type = true; + break; + } + } + + if (!context.m_found.type) { + // Try the modules next. + + do { + if (ClangModulesDeclVendor *modules_decl_vendor = + m_target->GetClangModulesDeclVendor()) { + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls)) + break; + + if (log) { + LLDB_LOGF(log, + " CAS::FEVD[%u] Matching entity found for \"%s\" in " + "the modules", + current_id, name.GetCString()); + } + + clang::NamedDecl *const decl_from_modules = decls[0]; + + if (llvm::isa<clang::TypeDecl>(decl_from_modules) || + llvm::isa<clang::ObjCContainerDecl>(decl_from_modules) || + llvm::isa<clang::EnumConstantDecl>(decl_from_modules)) { + clang::Decl *copied_decl = CopyDecl(decl_from_modules); + clang::NamedDecl *copied_named_decl = + copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr; + + if (!copied_named_decl) { + LLDB_LOGF( + log, + " CAS::FEVD[%u] - Couldn't export a type from the modules", + current_id); + + break; + } + + context.AddNamedDecl(copied_named_decl); + + context.m_found.type = true; + } + } + } while (false); + } + + if (!context.m_found.type) { + do { + // Couldn't find any types elsewhere. Try the Objective-C runtime if + // one exists. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime( + ObjCLanguageRuntime::Get(*process)); + + if (!language_runtime) + break; + + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); + + if (!decl_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + auto *clang_decl_vendor = llvm::cast<ClangDeclVendor>(decl_vendor); + if (!clang_decl_vendor->FindDecls(name, append, max_matches, decls)) + break; + + if (log) { + LLDB_LOGF( + log, + " CAS::FEVD[%u] Matching type found for \"%s\" in the runtime", + current_id, name.GetCString()); + } + + clang::Decl *copied_decl = CopyDecl(decls[0]); + clang::NamedDecl *copied_named_decl = + copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr; + + if (!copied_named_decl) { + LLDB_LOGF(log, + " CAS::FEVD[%u] - Couldn't export a type from the runtime", + current_id); + + break; + } + + context.AddNamedDecl(copied_named_decl); + } while (false); + } + + } while (false); +} + +template <class D> class TaggedASTDecl { +public: + TaggedASTDecl() : decl(nullptr) {} + TaggedASTDecl(D *_decl) : decl(_decl) {} + bool IsValid() const { return (decl != nullptr); } + bool IsInvalid() const { return !IsValid(); } + D *operator->() const { return decl; } + D *decl; +}; + +template <class D2, template <class D> class TD, class D1> +TD<D2> DynCast(TD<D1> source) { + return TD<D2>(dyn_cast<D2>(source.decl)); +} + +template <class D = Decl> class DeclFromParser; +template <class D = Decl> class DeclFromUser; + +template <class D> class DeclFromParser : public TaggedASTDecl<D> { +public: + DeclFromParser() : TaggedASTDecl<D>() {} + DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {} + + DeclFromUser<D> GetOrigin(ClangASTSource &source); +}; + +template <class D> class DeclFromUser : public TaggedASTDecl<D> { +public: + DeclFromUser() : TaggedASTDecl<D>() {} + DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {} + + DeclFromParser<D> Import(ClangASTSource &source); +}; + +template <class D> +DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTSource &source) { + ClangASTImporter::DeclOrigin origin = source.GetDeclOrigin(this->decl); + if (!origin.Valid()) + return DeclFromUser<D>(); + return DeclFromUser<D>(dyn_cast<D>(origin.decl)); +} + +template <class D> +DeclFromParser<D> DeclFromUser<D>::Import(ClangASTSource &source) { + DeclFromParser<> parser_generic_decl(source.CopyDecl(this->decl)); + if (parser_generic_decl.IsInvalid()) + return DeclFromParser<D>(); + return DeclFromParser<D>(dyn_cast<D>(parser_generic_decl.decl)); +} + +bool ClangASTSource::FindObjCMethodDeclsWithOrigin( + unsigned int current_id, NameSearchContext &context, + ObjCInterfaceDecl *original_interface_decl, const char *log_info) { + const DeclarationName &decl_name(context.m_decl_name); + clang::ASTContext *original_ctx = &original_interface_decl->getASTContext(); + + Selector original_selector; + + if (decl_name.isObjCZeroArgSelector()) { + IdentifierInfo *ident = &original_ctx->Idents.get(decl_name.getAsString()); + original_selector = original_ctx->Selectors.getSelector(0, &ident); + } else if (decl_name.isObjCOneArgSelector()) { + const std::string &decl_name_string = decl_name.getAsString(); + std::string decl_name_string_without_colon(decl_name_string.c_str(), + decl_name_string.length() - 1); + IdentifierInfo *ident = + &original_ctx->Idents.get(decl_name_string_without_colon); + original_selector = original_ctx->Selectors.getSelector(1, &ident); + } else { + SmallVector<IdentifierInfo *, 4> idents; + + clang::Selector sel = decl_name.getObjCSelector(); + + unsigned num_args = sel.getNumArgs(); + + for (unsigned i = 0; i != num_args; ++i) { + idents.push_back(&original_ctx->Idents.get(sel.getNameForSlot(i))); + } + + original_selector = + original_ctx->Selectors.getSelector(num_args, idents.data()); + } + + DeclarationName original_decl_name(original_selector); + + llvm::SmallVector<NamedDecl *, 1> methods; + + ClangASTContext::GetCompleteDecl(original_ctx, original_interface_decl); + + if (ObjCMethodDecl *instance_method_decl = + original_interface_decl->lookupInstanceMethod(original_selector)) { + methods.push_back(instance_method_decl); + } else if (ObjCMethodDecl *class_method_decl = + original_interface_decl->lookupClassMethod( + original_selector)) { + methods.push_back(class_method_decl); + } + + if (methods.empty()) { + return false; + } + + for (NamedDecl *named_decl : methods) { + if (!named_decl) + continue; + + ObjCMethodDecl *result_method = dyn_cast<ObjCMethodDecl>(named_decl); + + if (!result_method) + continue; + + Decl *copied_decl = CopyDecl(result_method); + + if (!copied_decl) + continue; + + ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl); + + if (!copied_method_decl) + continue; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOG(log, " CAS::FOMD[{0}] found ({1}) {2}", current_id, log_info, + ClangUtil::DumpDecl(copied_method_decl)); + + context.AddNamedDecl(copied_method_decl); + } + + return true; +} + +void ClangASTSource::FindObjCMethodDecls(NameSearchContext &context) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + const DeclarationName &decl_name(context.m_decl_name); + const DeclContext *decl_ctx(context.m_decl_context); + + const ObjCInterfaceDecl *interface_decl = + dyn_cast<ObjCInterfaceDecl>(decl_ctx); + + if (!interface_decl) + return; + + do { + ClangASTImporter::DeclOrigin original = m_ast_importer_sp->GetDeclOrigin(interface_decl); + + if (!original.Valid()) + break; + + ObjCInterfaceDecl *original_interface_decl = + dyn_cast<ObjCInterfaceDecl>(original.decl); + + if (FindObjCMethodDeclsWithOrigin(current_id, context, + original_interface_decl, "at origin")) + return; // found it, no need to look any further + } while (false); + + StreamString ss; + + if (decl_name.isObjCZeroArgSelector()) { + ss.Printf("%s", decl_name.getAsString().c_str()); + } else if (decl_name.isObjCOneArgSelector()) { + ss.Printf("%s", decl_name.getAsString().c_str()); + } else { + clang::Selector sel = decl_name.getObjCSelector(); + + for (unsigned i = 0, e = sel.getNumArgs(); i != e; ++i) { + llvm::StringRef r = sel.getNameForSlot(i); + ss.Printf("%s:", r.str().c_str()); + } + } + ss.Flush(); + + if (ss.GetString().contains("$__lldb")) + return; // we don't need any results + + ConstString selector_name(ss.GetString()); + + LLDB_LOGF(log, + "ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p " + "for selector [%s %s]", + current_id, static_cast<void *>(m_ast_context), + interface_decl->getNameAsString().c_str(), + selector_name.AsCString()); + SymbolContextList sc_list; + + const bool include_symbols = false; + const bool include_inlines = false; + + std::string interface_name = interface_decl->getNameAsString(); + + do { + StreamString ms; + ms.Printf("-[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString instance_method_name(ms.GetString()); + + sc_list.Clear(); + m_target->GetImages().FindFunctions( + instance_method_name, lldb::eFunctionNameTypeFull, include_symbols, + include_inlines, sc_list); + + if (sc_list.GetSize()) + break; + + ms.Clear(); + ms.Printf("+[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString class_method_name(ms.GetString()); + + sc_list.Clear(); + m_target->GetImages().FindFunctions( + class_method_name, lldb::eFunctionNameTypeFull, include_symbols, + include_inlines, sc_list); + + if (sc_list.GetSize()) + break; + + // Fall back and check for methods in categories. If we find methods this + // way, we need to check that they're actually in categories on the desired + // class. + + SymbolContextList candidate_sc_list; + + m_target->GetImages().FindFunctions( + selector_name, lldb::eFunctionNameTypeSelector, include_symbols, + include_inlines, candidate_sc_list); + + for (uint32_t ci = 0, ce = candidate_sc_list.GetSize(); ci != ce; ++ci) { + SymbolContext candidate_sc; + + if (!candidate_sc_list.GetContextAtIndex(ci, candidate_sc)) + continue; + + if (!candidate_sc.function) + continue; + + const char *candidate_name = candidate_sc.function->GetName().AsCString(); + + const char *cursor = candidate_name; + + if (*cursor != '+' && *cursor != '-') + continue; + + ++cursor; + + if (*cursor != '[') + continue; + + ++cursor; + + size_t interface_len = interface_name.length(); + + if (strncmp(cursor, interface_name.c_str(), interface_len)) + continue; + + cursor += interface_len; + + if (*cursor == ' ' || *cursor == '(') + sc_list.Append(candidate_sc); + } + } while (false); + + if (sc_list.GetSize()) { + // We found a good function symbol. Use that. + + for (uint32_t i = 0, e = sc_list.GetSize(); i != e; ++i) { + SymbolContext sc; + + if (!sc_list.GetContextAtIndex(i, sc)) + continue; + + if (!sc.function) + continue; + + CompilerDeclContext function_decl_ctx = sc.function->GetDeclContext(); + if (!function_decl_ctx) + continue; + + ObjCMethodDecl *method_decl = + ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx); + + if (!method_decl) + continue; + + ObjCInterfaceDecl *found_interface_decl = + method_decl->getClassInterface(); + + if (!found_interface_decl) + continue; + + if (found_interface_decl->getName() == interface_decl->getName()) { + Decl *copied_decl = CopyDecl(method_decl); + + if (!copied_decl) + continue; + + ObjCMethodDecl *copied_method_decl = + dyn_cast<ObjCMethodDecl>(copied_decl); + + if (!copied_method_decl) + continue; + + LLDB_LOG(log, " CAS::FOMD[{0}] found (in symbols)\n{1}", current_id, + ClangUtil::DumpDecl(copied_method_decl)); + + context.AddNamedDecl(copied_method_decl); + } + } + + return; + } + + // Try the debug information. + + do { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface( + const_cast<ObjCInterfaceDecl *>(interface_decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried + // in this scenario. + + DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl( + complete_interface_decl); + + if (complete_interface_decl == interface_decl) + break; // already checked this one + + LLDB_LOGF(log, + "CAS::FOPD[%d] trying origin " + "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, static_cast<void *>(complete_interface_decl), + static_cast<void *>(&complete_iface_decl->getASTContext())); + + FindObjCMethodDeclsWithOrigin(current_id, context, complete_interface_decl, + "in debug info"); + + return; + } while (false); + + do { + // Check the modules only if the debug information didn't have a complete + // interface. + + if (ClangModulesDeclVendor *modules_decl_vendor = + m_target->GetClangModulesDeclVendor()) { + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(interface_name, append, max_matches, + decls)) + break; + + ObjCInterfaceDecl *interface_decl_from_modules = + dyn_cast<ObjCInterfaceDecl>(decls[0]); + + if (!interface_decl_from_modules) + break; + + if (FindObjCMethodDeclsWithOrigin( + current_id, context, interface_decl_from_modules, "in modules")) + return; + } + } while (false); + + do { + // Check the runtime only if the debug information didn't have a complete + // interface and the modules don't get us anywhere. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process)); + + if (!language_runtime) + break; + + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); + + if (!decl_vendor) + break; + + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + auto *clang_decl_vendor = llvm::cast<ClangDeclVendor>(decl_vendor); + if (!clang_decl_vendor->FindDecls(interface_name, append, max_matches, + decls)) + break; + + ObjCInterfaceDecl *runtime_interface_decl = + dyn_cast<ObjCInterfaceDecl>(decls[0]); + + if (!runtime_interface_decl) + break; + + FindObjCMethodDeclsWithOrigin(current_id, context, runtime_interface_decl, + "in runtime"); + } while (false); +} + +static bool FindObjCPropertyAndIvarDeclsWithOrigin( + unsigned int current_id, NameSearchContext &context, ClangASTSource &source, + DeclFromUser<const ObjCInterfaceDecl> &origin_iface_decl) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (origin_iface_decl.IsInvalid()) + return false; + + std::string name_str = context.m_decl_name.getAsString(); + StringRef name(name_str); + IdentifierInfo &name_identifier( + origin_iface_decl->getASTContext().Idents.get(name)); + + DeclFromUser<ObjCPropertyDecl> origin_property_decl( + origin_iface_decl->FindPropertyDeclaration( + &name_identifier, ObjCPropertyQueryKind::OBJC_PR_query_instance)); + + bool found = false; + + if (origin_property_decl.IsValid()) { + DeclFromParser<ObjCPropertyDecl> parser_property_decl( + origin_property_decl.Import(source)); + if (parser_property_decl.IsValid()) { + LLDB_LOG(log, " CAS::FOPD[{0}] found\n{1}", current_id, + ClangUtil::DumpDecl(parser_property_decl.decl)); + + context.AddNamedDecl(parser_property_decl.decl); + found = true; + } + } + + DeclFromUser<ObjCIvarDecl> origin_ivar_decl( + origin_iface_decl->getIvarDecl(&name_identifier)); + + if (origin_ivar_decl.IsValid()) { + DeclFromParser<ObjCIvarDecl> parser_ivar_decl( + origin_ivar_decl.Import(source)); + if (parser_ivar_decl.IsValid()) { + if (log) { + LLDB_LOG(log, " CAS::FOPD[{0}] found\n{1}", current_id, + ClangUtil::DumpDecl(parser_ivar_decl.decl)); + } + + context.AddNamedDecl(parser_ivar_decl.decl); + found = true; + } + } + + return found; +} + +void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + DeclFromParser<const ObjCInterfaceDecl> parser_iface_decl( + cast<ObjCInterfaceDecl>(context.m_decl_context)); + DeclFromUser<const ObjCInterfaceDecl> origin_iface_decl( + parser_iface_decl.GetOrigin(*this)); + + ConstString class_name(parser_iface_decl->getNameAsString().c_str()); + + LLDB_LOGF(log, + "ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on " + "(ASTContext*)%p for '%s.%s'", + current_id, static_cast<void *>(m_ast_context), + parser_iface_decl->getNameAsString().c_str(), + context.m_decl_name.getAsString().c_str()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin( + current_id, context, *this, origin_iface_decl)) + return; + + LLDB_LOGF(log, + "CAS::FOPD[%d] couldn't find the property on origin " + "(ObjCInterfaceDecl*)%p/(ASTContext*)%p, searching " + "elsewhere...", + current_id, static_cast<const void *>(origin_iface_decl.decl), + static_cast<void *>(&origin_iface_decl->getASTContext())); + + SymbolContext null_sc; + TypeList type_list; + + do { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface( + const_cast<ObjCInterfaceDecl *>(parser_iface_decl.decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried + // in this scenario. + + DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl( + complete_interface_decl); + + if (complete_iface_decl.decl == origin_iface_decl.decl) + break; // already checked this one + + LLDB_LOGF(log, + "CAS::FOPD[%d] trying origin " + "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, static_cast<const void *>(complete_iface_decl.decl), + static_cast<void *>(&complete_iface_decl->getASTContext())); + + FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this, + complete_iface_decl); + + return; + } while (false); + + do { + // Check the modules only if the debug information didn't have a complete + // interface. + + ClangModulesDeclVendor *modules_decl_vendor = + m_target->GetClangModulesDeclVendor(); + + if (!modules_decl_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(class_name, append, max_matches, decls)) + break; + + DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_modules( + dyn_cast<ObjCInterfaceDecl>(decls[0])); + + if (!interface_decl_from_modules.IsValid()) + break; + + LLDB_LOGF( + log, + "CAS::FOPD[%d] trying module " + "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, static_cast<const void *>(interface_decl_from_modules.decl), + static_cast<void *>(&interface_decl_from_modules->getASTContext())); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this, + interface_decl_from_modules)) + return; + } while (false); + + do { + // Check the runtime only if the debug information didn't have a complete + // interface and nothing was in the modules. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return; + + ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process)); + + if (!language_runtime) + return; + + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); + + if (!decl_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + auto *clang_decl_vendor = llvm::cast<ClangDeclVendor>(decl_vendor); + if (!clang_decl_vendor->FindDecls(class_name, append, max_matches, decls)) + break; + + DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_runtime( + dyn_cast<ObjCInterfaceDecl>(decls[0])); + + if (!interface_decl_from_runtime.IsValid()) + break; + + LLDB_LOGF( + log, + "CAS::FOPD[%d] trying runtime " + "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, static_cast<const void *>(interface_decl_from_runtime.decl), + static_cast<void *>(&interface_decl_from_runtime->getASTContext())); + + if (FindObjCPropertyAndIvarDeclsWithOrigin( + current_id, context, *this, interface_decl_from_runtime)) + return; + } while (false); +} + +typedef llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsetMap; +typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetMap; + +template <class D, class O> +static bool ImportOffsetMap(llvm::DenseMap<const D *, O> &destination_map, + llvm::DenseMap<const D *, O> &source_map, + ClangASTSource &source) { + // When importing fields into a new record, clang has a hard requirement that + // fields be imported in field offset order. Since they are stored in a + // DenseMap with a pointer as the key type, this means we cannot simply + // iterate over the map, as the order will be non-deterministic. Instead we + // have to sort by the offset and then insert in sorted order. + typedef llvm::DenseMap<const D *, O> MapType; + typedef typename MapType::value_type PairType; + std::vector<PairType> sorted_items; + sorted_items.reserve(source_map.size()); + sorted_items.assign(source_map.begin(), source_map.end()); + llvm::sort(sorted_items.begin(), sorted_items.end(), + [](const PairType &lhs, const PairType &rhs) { + return lhs.second < rhs.second; + }); + + for (const auto &item : sorted_items) { + DeclFromUser<D> user_decl(const_cast<D *>(item.first)); + DeclFromParser<D> parser_decl(user_decl.Import(source)); + if (parser_decl.IsInvalid()) + return false; + destination_map.insert( + std::pair<const D *, O>(parser_decl.decl, item.second)); + } + + return true; +} + +template <bool IsVirtual> +bool ExtractBaseOffsets(const ASTRecordLayout &record_layout, + DeclFromUser<const CXXRecordDecl> &record, + BaseOffsetMap &base_offsets) { + for (CXXRecordDecl::base_class_const_iterator + bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()), + be = (IsVirtual ? record->vbases_end() : record->bases_end()); + bi != be; ++bi) { + if (!IsVirtual && bi->isVirtual()) + continue; + + const clang::Type *origin_base_type = bi->getType().getTypePtr(); + const clang::RecordType *origin_base_record_type = + origin_base_type->getAs<RecordType>(); + + if (!origin_base_record_type) + return false; + + DeclFromUser<RecordDecl> origin_base_record( + origin_base_record_type->getDecl()); + + if (origin_base_record.IsInvalid()) + return false; + + DeclFromUser<CXXRecordDecl> origin_base_cxx_record( + DynCast<CXXRecordDecl>(origin_base_record)); + + if (origin_base_cxx_record.IsInvalid()) + return false; + + CharUnits base_offset; + + if (IsVirtual) + base_offset = + record_layout.getVBaseClassOffset(origin_base_cxx_record.decl); + else + base_offset = + record_layout.getBaseClassOffset(origin_base_cxx_record.decl); + + base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>( + origin_base_cxx_record.decl, base_offset)); + } + + return true; +} + +bool ClangASTSource::layoutRecordType(const RecordDecl *record, uint64_t &size, + uint64_t &alignment, + FieldOffsetMap &field_offsets, + BaseOffsetMap &base_offsets, + BaseOffsetMap &virtual_base_offsets) { + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOGF(log, + "LayoutRecordType[%u] on (ASTContext*)%p for (RecordDecl*)%p " + "[name = '%s']", + current_id, static_cast<void *>(m_ast_context), + static_cast<const void *>(record), + record->getNameAsString().c_str()); + + DeclFromParser<const RecordDecl> parser_record(record); + DeclFromUser<const RecordDecl> origin_record( + parser_record.GetOrigin(*this)); + + if (origin_record.IsInvalid()) + return false; + + FieldOffsetMap origin_field_offsets; + BaseOffsetMap origin_base_offsets; + BaseOffsetMap origin_virtual_base_offsets; + + ClangASTContext::GetCompleteDecl( + &origin_record->getASTContext(), + const_cast<RecordDecl *>(origin_record.decl)); + + clang::RecordDecl *definition = origin_record.decl->getDefinition(); + if (!definition || !definition->isCompleteDefinition()) + return false; + + const ASTRecordLayout &record_layout( + origin_record->getASTContext().getASTRecordLayout(origin_record.decl)); + + int field_idx = 0, field_count = record_layout.getFieldCount(); + + for (RecordDecl::field_iterator fi = origin_record->field_begin(), + fe = origin_record->field_end(); + fi != fe; ++fi) { + if (field_idx >= field_count) + return false; // Layout didn't go well. Bail out. + + uint64_t field_offset = record_layout.getFieldOffset(field_idx); + + origin_field_offsets.insert( + std::pair<const FieldDecl *, uint64_t>(*fi, field_offset)); + + field_idx++; + } + + lldbassert(&record->getASTContext() == m_ast_context); + + DeclFromUser<const CXXRecordDecl> origin_cxx_record( + DynCast<const CXXRecordDecl>(origin_record)); + + if (origin_cxx_record.IsValid()) { + if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record, + origin_base_offsets) || + !ExtractBaseOffsets<true>(record_layout, origin_cxx_record, + origin_virtual_base_offsets)) + return false; + } + + if (!ImportOffsetMap(field_offsets, origin_field_offsets, *this) || + !ImportOffsetMap(base_offsets, origin_base_offsets, *this) || + !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets, + *this)) + return false; + + size = record_layout.getSize().getQuantity() * m_ast_context->getCharWidth(); + alignment = record_layout.getAlignment().getQuantity() * + m_ast_context->getCharWidth(); + + if (log) { + LLDB_LOGF(log, "LRT[%u] returned:", current_id); + LLDB_LOGF(log, "LRT[%u] Original = (RecordDecl*)%p", current_id, + static_cast<const void *>(origin_record.decl)); + LLDB_LOGF(log, "LRT[%u] Size = %" PRId64, current_id, size); + LLDB_LOGF(log, "LRT[%u] Alignment = %" PRId64, current_id, alignment); + LLDB_LOGF(log, "LRT[%u] Fields:", current_id); + for (RecordDecl::field_iterator fi = record->field_begin(), + fe = record->field_end(); + fi != fe; ++fi) { + LLDB_LOGF(log, + "LRT[%u] (FieldDecl*)%p, Name = '%s', Offset = %" PRId64 + " bits", + current_id, static_cast<void *>(*fi), + fi->getNameAsString().c_str(), field_offsets[*fi]); + } + DeclFromParser<const CXXRecordDecl> parser_cxx_record = + DynCast<const CXXRecordDecl>(parser_record); + if (parser_cxx_record.IsValid()) { + LLDB_LOGF(log, "LRT[%u] Bases:", current_id); + for (CXXRecordDecl::base_class_const_iterator + bi = parser_cxx_record->bases_begin(), + be = parser_cxx_record->bases_end(); + bi != be; ++bi) { + bool is_virtual = bi->isVirtual(); + + QualType base_type = bi->getType(); + const RecordType *base_record_type = base_type->getAs<RecordType>(); + DeclFromParser<RecordDecl> base_record(base_record_type->getDecl()); + DeclFromParser<CXXRecordDecl> base_cxx_record = + DynCast<CXXRecordDecl>(base_record); + + LLDB_LOGF( + log, + "LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64 + " chars", + current_id, (is_virtual ? "Virtual " : ""), + static_cast<void *>(base_cxx_record.decl), + base_cxx_record.decl->getNameAsString().c_str(), + (is_virtual + ? virtual_base_offsets[base_cxx_record.decl].getQuantity() + : base_offsets[base_cxx_record.decl].getQuantity())); + } + } else { + LLDB_LOGF(log, "LRD[%u] Not a CXXRecord, so no bases", current_id); + } + } + + return true; +} + +void ClangASTSource::CompleteNamespaceMap( + ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name, + ClangASTImporter::NamespaceMapSP &parent_map) const { + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (log) { + if (parent_map && parent_map->size()) + LLDB_LOGF(log, + "CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for " + "namespace %s in namespace %s", + current_id, static_cast<void *>(m_ast_context), + name.GetCString(), + parent_map->begin()->second.GetName().AsCString()); + else + LLDB_LOGF(log, + "CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for " + "namespace %s", + current_id, static_cast<void *>(m_ast_context), + name.GetCString()); + } + + if (parent_map) { + for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(), + e = parent_map->end(); + i != e; ++i) { + CompilerDeclContext found_namespace_decl; + + lldb::ModuleSP module_sp = i->first; + CompilerDeclContext module_parent_namespace_decl = i->second; + + SymbolFile *symbol_file = module_sp->GetSymbolFile(); + + if (!symbol_file) + continue; + + found_namespace_decl = + symbol_file->FindNamespace(name, &module_parent_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>( + module_sp, found_namespace_decl)); + + LLDB_LOGF(log, " CMN[%u] Found namespace %s in module %s", current_id, + name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } else { + const ModuleList &target_images = m_target->GetImages(); + std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex()); + + CompilerDeclContext null_namespace_decl; + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + CompilerDeclContext found_namespace_decl; + + SymbolFile *symbol_file = image->GetSymbolFile(); + + if (!symbol_file) + continue; + + found_namespace_decl = + symbol_file->FindNamespace(name, &null_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>( + image, found_namespace_decl)); + + LLDB_LOGF(log, " CMN[%u] Found namespace %s in module %s", current_id, + name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } +} + +NamespaceDecl *ClangASTSource::AddNamespace( + NameSearchContext &context, + ClangASTImporter::NamespaceMapSP &namespace_decls) { + if (!namespace_decls) + return nullptr; + + const CompilerDeclContext &namespace_decl = namespace_decls->begin()->second; + + clang::ASTContext *src_ast = + ClangASTContext::DeclContextGetClangASTContext(namespace_decl); + if (!src_ast) + return nullptr; + clang::NamespaceDecl *src_namespace_decl = + ClangASTContext::DeclContextGetAsNamespaceDecl(namespace_decl); + + if (!src_namespace_decl) + return nullptr; + + Decl *copied_decl = CopyDecl(src_namespace_decl); + + if (!copied_decl) + return nullptr; + + NamespaceDecl *copied_namespace_decl = dyn_cast<NamespaceDecl>(copied_decl); + + if (!copied_namespace_decl) + return nullptr; + + context.m_decls.push_back(copied_namespace_decl); + + m_ast_importer_sp->RegisterNamespaceMap(copied_namespace_decl, + namespace_decls); + + return dyn_cast<NamespaceDecl>(copied_decl); +} + +clang::Decl *ClangASTSource::CopyDecl(Decl *src_decl) { + if (m_ast_importer_sp) { + return m_ast_importer_sp->CopyDecl(m_ast_context, src_decl); + } else { + lldbassert(0 && "No mechanism for copying a decl!"); + return nullptr; + } +} + +ClangASTImporter::DeclOrigin ClangASTSource::GetDeclOrigin(const clang::Decl *decl) { + if (m_ast_importer_sp) { + return m_ast_importer_sp->GetDeclOrigin(decl); + } else { + // this can happen early enough that no ExternalASTSource is installed. + return ClangASTImporter::DeclOrigin(); + } +} + +CompilerType ClangASTSource::GuardedCopyType(const CompilerType &src_type) { + ClangASTContext *src_ast = + llvm::dyn_cast_or_null<ClangASTContext>(src_type.GetTypeSystem()); + if (src_ast == nullptr) + return CompilerType(); + + SetImportInProgress(true); + + QualType copied_qual_type; + + if (m_ast_importer_sp) { + copied_qual_type = ClangUtil::GetQualType( + m_ast_importer_sp->CopyType(*m_clang_ast_context, src_type)); + } else { + lldbassert(0 && "No mechanism for copying a type!"); + return CompilerType(); + } + + SetImportInProgress(false); + + if (copied_qual_type.getAsOpaquePtr() && + copied_qual_type->getCanonicalTypeInternal().isNull()) + // this shouldn't happen, but we're hardening because the AST importer + // seems to be generating bad types on occasion. + return CompilerType(); + + return m_clang_ast_context->GetType(copied_qual_type); +} + +clang::NamedDecl *NameSearchContext::AddVarDecl(const CompilerType &type) { + assert(type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return nullptr; + + ClangASTContext *lldb_ast = + llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem()); + if (!lldb_ast) + return nullptr; + + IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo(); + + clang::ASTContext &ast = lldb_ast->getASTContext(); + + clang::NamedDecl *Decl = VarDecl::Create( + ast, const_cast<DeclContext *>(m_decl_context), SourceLocation(), + SourceLocation(), ii, ClangUtil::GetQualType(type), nullptr, SC_Static); + m_decls.push_back(Decl); + + return Decl; +} + +clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type, + bool extern_c) { + assert(type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return nullptr; + + if (m_function_types.count(type)) + return nullptr; + + ClangASTContext *lldb_ast = + llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem()); + if (!lldb_ast) + return nullptr; + + m_function_types.insert(type); + + QualType qual_type(ClangUtil::GetQualType(type)); + + clang::ASTContext &ast = lldb_ast->getASTContext(); + + const bool isInlineSpecified = false; + const bool hasWrittenPrototype = true; + const bool isConstexprSpecified = false; + + clang::DeclContext *context = const_cast<DeclContext *>(m_decl_context); + + if (extern_c) { + context = LinkageSpecDecl::Create( + ast, context, SourceLocation(), SourceLocation(), + clang::LinkageSpecDecl::LanguageIDs::lang_c, false); + } + + // Pass the identifier info for functions the decl_name is needed for + // operators + clang::DeclarationName decl_name = + m_decl_name.getNameKind() == DeclarationName::Identifier + ? m_decl_name.getAsIdentifierInfo() + : m_decl_name; + + clang::FunctionDecl *func_decl = FunctionDecl::Create( + ast, context, SourceLocation(), SourceLocation(), decl_name, qual_type, + nullptr, SC_Extern, isInlineSpecified, hasWrittenPrototype, + isConstexprSpecified ? CSK_constexpr : CSK_unspecified); + + // We have to do more than just synthesize the FunctionDecl. We have to + // synthesize ParmVarDecls for all of the FunctionDecl's arguments. To do + // this, we raid the function's FunctionProtoType for types. + + const FunctionProtoType *func_proto_type = + qual_type.getTypePtr()->getAs<FunctionProtoType>(); + + if (func_proto_type) { + unsigned NumArgs = func_proto_type->getNumParams(); + unsigned ArgIndex; + + SmallVector<ParmVarDecl *, 5> parm_var_decls; + + for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) { + QualType arg_qual_type(func_proto_type->getParamType(ArgIndex)); + + parm_var_decls.push_back( + ParmVarDecl::Create(ast, const_cast<DeclContext *>(context), + SourceLocation(), SourceLocation(), nullptr, + arg_qual_type, nullptr, SC_Static, nullptr)); + } + + func_decl->setParams(ArrayRef<ParmVarDecl *>(parm_var_decls)); + } else { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOGF(log, "Function type wasn't a FunctionProtoType"); + } + + // If this is an operator (e.g. operator new or operator==), only insert the + // declaration we inferred from the symbol if we can provide the correct + // number of arguments. We shouldn't really inject random decl(s) for + // functions that are analyzed semantically in a special way, otherwise we + // will crash in clang. + clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; + if (func_proto_type && + ClangASTContext::IsOperator(decl_name.getAsString().c_str(), op_kind)) { + if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount( + false, op_kind, func_proto_type->getNumParams())) + return nullptr; + } + m_decls.push_back(func_decl); + + return func_decl; +} + +clang::NamedDecl *NameSearchContext::AddGenericFunDecl() { + FunctionProtoType::ExtProtoInfo proto_info; + + proto_info.Variadic = true; + + QualType generic_function_type(m_ast_source.m_ast_context->getFunctionType( + m_ast_source.m_ast_context->UnknownAnyTy, // result + ArrayRef<QualType>(), // argument types + proto_info)); + + return AddFunDecl( + m_ast_source.m_clang_ast_context->GetType(generic_function_type), true); +} + +clang::NamedDecl * +NameSearchContext::AddTypeDecl(const CompilerType &clang_type) { + if (ClangUtil::IsClangType(clang_type)) { + QualType qual_type = ClangUtil::GetQualType(clang_type); + + if (const TypedefType *typedef_type = + llvm::dyn_cast<TypedefType>(qual_type)) { + TypedefNameDecl *typedef_name_decl = typedef_type->getDecl(); + + m_decls.push_back(typedef_name_decl); + + return (NamedDecl *)typedef_name_decl; + } else if (const TagType *tag_type = qual_type->getAs<TagType>()) { + TagDecl *tag_decl = tag_type->getDecl(); + + m_decls.push_back(tag_decl); + + return tag_decl; + } else if (const ObjCObjectType *objc_object_type = + qual_type->getAs<ObjCObjectType>()) { + ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface(); + + m_decls.push_back((NamedDecl *)interface_decl); + + return (NamedDecl *)interface_decl; + } + } + return nullptr; +} + +void NameSearchContext::AddLookupResult(clang::DeclContextLookupResult result) { + for (clang::NamedDecl *decl : result) + m_decls.push_back(decl); +} + +void NameSearchContext::AddNamedDecl(clang::NamedDecl *decl) { + m_decls.push_back(decl); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h new file mode 100644 index 00000000000..3149b4266b2 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h @@ -0,0 +1,484 @@ +//===-- ClangASTSource.h ----------------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangASTSource_h_ +#define liblldb_ClangASTSource_h_ + +#include <set> + +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Target.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/IdentifierTable.h" + +#include "llvm/ADT/SmallSet.h" + +namespace lldb_private { + +/// \class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h" +/// Provider for named objects defined in the debug info for Clang +/// +/// As Clang parses an expression, it may encounter names that are not defined +/// inside the expression, including variables, functions, and types. Clang +/// knows the name it is looking for, but nothing else. The ExternalSemaSource +/// class provides Decls (VarDecl, FunDecl, TypeDecl) to Clang for these +/// names, consulting the ClangExpressionDeclMap to do the actual lookups. +class ClangASTSource : public clang::ExternalASTSource, + public ClangASTImporter::MapCompleter { +public: + /// Constructor + /// + /// Initializes class variables. + /// + /// \param[in] target + /// A reference to the target containing debug information to use. + /// + /// \param[in] importer + /// The ClangASTImporter to use. + ClangASTSource(const lldb::TargetSP &target, + const lldb::ClangASTImporterSP &importer); + + /// Destructor + ~ClangASTSource() override; + + /// Interface stubs. + clang::Decl *GetExternalDecl(uint32_t) override { return nullptr; } + clang::Stmt *GetExternalDeclStmt(uint64_t) override { return nullptr; } + clang::Selector GetExternalSelector(uint32_t) override { + return clang::Selector(); + } + uint32_t GetNumExternalSelectors() override { return 0; } + clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + return nullptr; + } + void MaterializeVisibleDecls(const clang::DeclContext *DC) { return; } + + void InstallASTContext(ClangASTContext &ast_context); + + // + // APIs for ExternalASTSource + // + + /// Look up all Decls that match a particular name. Only handles + /// Identifiers and DeclContexts that are either NamespaceDecls or + /// TranslationUnitDecls. Calls SetExternalVisibleDeclsForName with the + /// result. + /// + /// The work for this function is done by + /// void FindExternalVisibleDecls (NameSearchContext &); + /// + /// \param[in] DC + /// The DeclContext to register the found Decls in. + /// + /// \param[in] Name + /// The name to find entries for. + /// + /// \return + /// Whatever SetExternalVisibleDeclsForName returns. + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override; + + /// Enumerate all Decls in a given lexical context. + /// + /// \param[in] DC + /// The DeclContext being searched. + /// + /// \param[in] IsKindWeWant + /// A callback function that returns true given the + /// DeclKinds of desired Decls, and false otherwise. + /// + /// \param[in] Decls + /// A vector that is filled in with matching Decls. + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant, + llvm::SmallVectorImpl<clang::Decl *> &Decls) override; + + /// Specify the layout of the contents of a RecordDecl. + /// + /// \param[in] Record + /// The record (in the parser's AST context) that needs to be + /// laid out. + /// + /// \param[out] Size + /// The total size of the record in bits. + /// + /// \param[out] Alignment + /// The alignment of the record in bits. + /// + /// \param[in] FieldOffsets + /// A map that must be populated with pairs of the record's + /// fields (in the parser's AST context) and their offsets + /// (measured in bits). + /// + /// \param[in] BaseOffsets + /// A map that must be populated with pairs of the record's + /// C++ concrete base classes (in the parser's AST context, + /// and only if the record is a CXXRecordDecl and has base + /// classes) and their offsets (measured in bytes). + /// + /// \param[in] VirtualBaseOffsets + /// A map that must be populated with pairs of the record's + /// C++ virtual base classes (in the parser's AST context, + /// and only if the record is a CXXRecordDecl and has base + /// classes) and their offsets (measured in bytes). + /// + /// \return + /// True <=> the layout is valid. + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &BaseOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &VirtualBaseOffsets) override; + + /// Complete a TagDecl. + /// + /// \param[in] Tag + /// The Decl to be completed in place. + void CompleteType(clang::TagDecl *Tag) override; + + /// Complete an ObjCInterfaceDecl. + /// + /// \param[in] Class + /// The Decl to be completed in place. + void CompleteType(clang::ObjCInterfaceDecl *Class) override; + + /// Called on entering a translation unit. Tells Clang by calling + /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that + /// this object has something to say about undefined names. + /// + /// \param[in] Consumer + /// Unused. + void StartTranslationUnit(clang::ASTConsumer *Consumer) override; + + // + // APIs for NamespaceMapCompleter + // + + /// Look up the modules containing a given namespace and put the appropriate + /// entries in the namespace map. + /// + /// \param[in] namespace_map + /// The map to be completed. + /// + /// \param[in] name + /// The name of the namespace to be found. + /// + /// \param[in] parent_map + /// The map for the namespace's parent namespace, if there is + /// one. + void CompleteNamespaceMap( + ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name, + ClangASTImporter::NamespaceMapSP &parent_map) const override; + + // + // Helper APIs + // + + clang::NamespaceDecl * + AddNamespace(NameSearchContext &context, + ClangASTImporter::NamespaceMapSP &namespace_decls); + + /// The worker function for FindExternalVisibleDeclsByName. + /// + /// \param[in] context + /// The NameSearchContext to use when filing results. + virtual void FindExternalVisibleDecls(NameSearchContext &context); + + clang::Sema *getSema(); + + void SetImportInProgress(bool import_in_progress) { + m_import_in_progress = import_in_progress; + } + bool GetImportInProgress() { return m_import_in_progress; } + + void SetLookupsEnabled(bool lookups_enabled) { + m_lookups_enabled = lookups_enabled; + } + bool GetLookupsEnabled() { return m_lookups_enabled; } + + /// \class ClangASTSourceProxy ClangASTSource.h + /// "lldb/Expression/ClangASTSource.h" Proxy for ClangASTSource + /// + /// Clang AST contexts like to own their AST sources, so this is a state- + /// free proxy object. + class ClangASTSourceProxy : public clang::ExternalASTSource { + public: + ClangASTSourceProxy(ClangASTSource &original) : m_original(original) {} + + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + return m_original.FindExternalVisibleDeclsByName(DC, Name); + } + + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant, + llvm::SmallVectorImpl<clang::Decl *> &Decls) override { + return m_original.FindExternalLexicalDecls(DC, IsKindWeWant, Decls); + } + + void CompleteType(clang::TagDecl *Tag) override { + return m_original.CompleteType(Tag); + } + + void CompleteType(clang::ObjCInterfaceDecl *Class) override { + return m_original.CompleteType(Class); + } + + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &BaseOffsets, + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> + &VirtualBaseOffsets) override { + return m_original.layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets); + } + + void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + return m_original.StartTranslationUnit(Consumer); + } + + private: + ClangASTSource &m_original; + }; + + clang::ExternalASTSource *CreateProxy() { + return new ClangASTSourceProxy(*this); + } + +protected: + /// Look for the complete version of an Objective-C interface, and return it + /// if found. + /// + /// \param[in] interface_decl + /// An ObjCInterfaceDecl that may not be the complete one. + /// + /// \return + /// NULL if the complete interface couldn't be found; + /// the complete interface otherwise. + clang::ObjCInterfaceDecl * + GetCompleteObjCInterface(const clang::ObjCInterfaceDecl *interface_decl); + + /// Find all entities matching a given name in a given module, using a + /// NameSearchContext to make Decls for them. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] module + /// If non-NULL, the module to query. + /// + /// \param[in] namespace_decl + /// If valid and module is non-NULL, the parent namespace. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void FindExternalVisibleDecls(NameSearchContext &context, + lldb::ModuleSP module, + CompilerDeclContext &namespace_decl, + unsigned int current_id); + + /// Find all Objective-C methods matching a given selector. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// Its m_decl_name contains the selector and its m_decl_context + /// is the containing object. + void FindObjCMethodDecls(NameSearchContext &context); + + /// Find all Objective-C properties and ivars with a given name. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// Its m_decl_name contains the name and its m_decl_context + /// is the containing object. + void FindObjCPropertyAndIvarDecls(NameSearchContext &context); + + /// A wrapper for ClangASTContext::CopyType that sets a flag that + /// indicates that we should not respond to queries during import. + /// + /// \param[in] src_type + /// The source type. + /// + /// \return + /// The imported type. + CompilerType GuardedCopyType(const CompilerType &src_type); + +public: + /// Returns true if a name should be ignored by name lookup. + /// + /// \param[in] name + /// The name to be considered. + /// + /// \param[in] ignore_all_dollar_names + /// True if $-names of all sorts should be ignored. + /// + /// \return + /// True if the name is one of a class of names that are ignored by + /// global lookup for performance reasons. + bool IgnoreName(const ConstString name, bool ignore_all_dollar_names); + +public: + /// Copies a single Decl into the parser's AST context. + /// + /// \param[in] src_decl + /// The Decl to copy. + /// + /// \return + /// A copy of the Decl in m_ast_context, or NULL if the copy failed. + clang::Decl *CopyDecl(clang::Decl *src_decl); + + /// Determined the origin of a single Decl, if it can be found. + /// + /// \param[in] decl + /// The Decl whose origin is to be found. + /// + /// \param[out] original_decl + /// A pointer whose target is filled in with the original Decl. + /// + /// \param[in] original_ctx + /// A pointer whose target is filled in with the original's ASTContext. + /// + /// \return + /// True if lookup succeeded; false otherwise. + ClangASTImporter::DeclOrigin GetDeclOrigin(const clang::Decl *decl); + +protected: + bool FindObjCMethodDeclsWithOrigin( + unsigned int current_id, NameSearchContext &context, + clang::ObjCInterfaceDecl *original_interface_decl, const char *log_info); + + friend struct NameSearchContext; + + bool m_import_in_progress; + bool m_lookups_enabled; + + /// The target to use in finding variables and types. + const lldb::TargetSP m_target; + /// The AST context requests are coming in for. + clang::ASTContext *m_ast_context; + /// The ClangASTContext for m_ast_context. + ClangASTContext *m_clang_ast_context; + /// The file manager paired with the AST context. + clang::FileManager *m_file_manager; + /// The target's AST importer. + lldb::ClangASTImporterSP m_ast_importer_sp; + std::set<const clang::Decl *> m_active_lexical_decls; + std::set<const char *> m_active_lookups; +}; + +/// \class NameSearchContext ClangASTSource.h +/// "lldb/Expression/ClangASTSource.h" Container for all objects relevant to a +/// single name lookup +/// +/// LLDB needs to create Decls for entities it finds. This class communicates +/// what name is being searched for and provides helper functions to construct +/// Decls given appropriate type information. +struct NameSearchContext { + /// The AST source making the request. + ClangASTSource &m_ast_source; + /// The list of declarations already constructed. + llvm::SmallVectorImpl<clang::NamedDecl *> &m_decls; + /// The mapping of all namespaces found for this request back to their + /// modules. + ClangASTImporter::NamespaceMapSP m_namespace_map; + /// The name being looked for. + const clang::DeclarationName &m_decl_name; + /// The DeclContext to put declarations into. + const clang::DeclContext *m_decl_context; + /// All the types of functions that have been reported, so we don't + /// report conflicts. + llvm::SmallSet<CompilerType, 5> m_function_types; + + struct { + bool variable : 1; + bool function_with_type_info : 1; + bool function : 1; + bool local_vars_nsp : 1; + bool type : 1; + } m_found; + + /// Constructor + /// + /// Initializes class variables. + /// + /// \param[in] astSource + /// A reference to the AST source making a request. + /// + /// \param[in] decls + /// A reference to a list into which new Decls will be placed. This + /// list is typically empty when the function is called. + /// + /// \param[in] name + /// The name being searched for (always an Identifier). + /// + /// \param[in] dc + /// The DeclContext to register Decls in. + NameSearchContext(ClangASTSource &astSource, + llvm::SmallVectorImpl<clang::NamedDecl *> &decls, + clang::DeclarationName &name, const clang::DeclContext *dc) + : m_ast_source(astSource), m_decls(decls), m_decl_name(name), + m_decl_context(dc) { + memset(&m_found, 0, sizeof(m_found)); + } + + /// Create a VarDecl with the name being searched for and the provided type + /// and register it in the right places. + /// + /// \param[in] type + /// The opaque QualType for the VarDecl being registered. + clang::NamedDecl *AddVarDecl(const CompilerType &type); + + /// Create a FunDecl with the name being searched for and the provided type + /// and register it in the right places. + /// + /// \param[in] type + /// The opaque QualType for the FunDecl being registered. + /// + /// \param[in] extern_c + /// If true, build an extern "C" linkage specification for this. + clang::NamedDecl *AddFunDecl(const CompilerType &type, bool extern_c = false); + + /// Create a FunDecl with the name being searched for and generic type (i.e. + /// intptr_t NAME_GOES_HERE(...)) and register it in the right places. + clang::NamedDecl *AddGenericFunDecl(); + + /// Create a TypeDecl with the name being searched for and the provided type + /// and register it in the right places. + /// + /// \param[in] compiler_type + /// The opaque QualType for the TypeDecl being registered. + clang::NamedDecl *AddTypeDecl(const CompilerType &compiler_type); + + /// Add Decls from the provided DeclContextLookupResult to the list of + /// results. + /// + /// \param[in] result + /// The DeclContextLookupResult, usually returned as the result + /// of querying a DeclContext. + void AddLookupResult(clang::DeclContextLookupResult result); + + /// Add a NamedDecl to the list of results. + /// + /// \param[in] decl + /// The NamedDecl, usually returned as the result + /// of querying a DeclContext. + void AddNamedDecl(clang::NamedDecl *decl); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangASTSource_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.cpp new file mode 100644 index 00000000000..c87507a2585 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.cpp @@ -0,0 +1,30 @@ +//===-- ClangDeclVendor.cpp -------------------------------------*- C++ -*-===// +// +// 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 "Plugins/ExpressionParser/Clang/ClangDeclVendor.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Utility/ConstString.h" + +using namespace lldb_private; + +uint32_t ClangDeclVendor::FindDecls(ConstString name, bool append, + uint32_t max_matches, + std::vector<clang::NamedDecl *> &decls) { + if (!append) + decls.clear(); + + std::vector<CompilerDecl> compiler_decls; + uint32_t ret = FindDecls(name, /*append*/ false, max_matches, compiler_decls); + for (CompilerDecl compiler_decl : compiler_decls) { + clang::Decl *d = static_cast<clang::Decl *>(compiler_decl.GetOpaqueDecl()); + clang::NamedDecl *nd = llvm::cast<clang::NamedDecl>(d); + decls.push_back(nd); + } + return ret; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.h new file mode 100644 index 00000000000..0c888de0884 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDeclVendor.h @@ -0,0 +1,39 @@ +//===-- ClangDeclVendor.h ---------------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangDeclVendor_h_ +#define liblldb_ClangDeclVendor_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/DeclVendor.h" + +namespace lldb_private { + +// A clang specialized extension to DeclVendor. +class ClangDeclVendor : public DeclVendor { +public: + ClangDeclVendor(DeclVendorKind kind) : DeclVendor(kind) {} + + virtual ~ClangDeclVendor() {} + + using DeclVendor::FindDecls; + + uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches, + std::vector<clang::NamedDecl *> &decls); + + static bool classof(const DeclVendor *vendor) { + return vendor->GetKind() >= eClangDeclVendor && + vendor->GetKind() < eLastClangDeclVendor; + } + +private: + DISALLOW_COPY_AND_ASSIGN(ClangDeclVendor); +}; +} // namespace lldb_private + +#endif diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h new file mode 100644 index 00000000000..48cd1c4b99f --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h @@ -0,0 +1,50 @@ +//===-- ClangDiagnostic.h ---------------------------------------*- C++ -*-===// +// +// 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_ClangDiagnostic_h +#define lldb_ClangDiagnostic_h + +#include <vector> + +#include "clang/Basic/Diagnostic.h" + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +#include "lldb/Expression/DiagnosticManager.h" + +namespace lldb_private { + +class ClangDiagnostic : public Diagnostic { +public: + typedef std::vector<clang::FixItHint> FixItList; + + static inline bool classof(const ClangDiagnostic *) { return true; } + static inline bool classof(const Diagnostic *diag) { + return diag->getKind() == eDiagnosticOriginClang; + } + + ClangDiagnostic(llvm::StringRef message, DiagnosticSeverity severity, + uint32_t compiler_id) + : Diagnostic(message, severity, eDiagnosticOriginClang, compiler_id) {} + + ~ClangDiagnostic() override = default; + + bool HasFixIts() const override { return !m_fixit_vec.empty(); } + + void AddFixitHint(const clang::FixItHint &fixit) { + m_fixit_vec.push_back(fixit); + } + + const FixItList &FixIts() const { return m_fixit_vec; } +private: + FixItList m_fixit_vec; +}; + +} // namespace lldb_private +#endif /* lldb_ClangDiagnostic_h */ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp new file mode 100644 index 00000000000..bf3023be5f6 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -0,0 +1,2027 @@ +//===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// +// +// 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 "ClangExpressionDeclMap.h" + +#include "ClangASTSource.h" +#include "ClangModulesDeclVendor.h" +#include "ClangPersistentVariables.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/CompilerDecl.h" +#include "lldb/Symbol/CompilerDeclContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-private.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/RecursiveASTVisitor.h" + +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +namespace { +const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars"; +} // anonymous namespace + +ClangExpressionDeclMap::ClangExpressionDeclMap( + bool keep_result_in_memory, + Materializer::PersistentVariableDelegate *result_delegate, + const lldb::TargetSP &target, const lldb::ClangASTImporterSP &importer, + ValueObject *ctx_obj) + : ClangASTSource(target, importer), m_found_entities(), m_struct_members(), + m_keep_result_in_memory(keep_result_in_memory), + m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), m_parser_vars(), + m_struct_vars() { + EnableStructVars(); +} + +ClangExpressionDeclMap::~ClangExpressionDeclMap() { + // Note: The model is now that the parser's AST context and all associated + // data does not vanish until the expression has been executed. This means + // that valuable lookup data (like namespaces) doesn't vanish, but + + DidParse(); + DisableStructVars(); +} + +bool ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx, + Materializer *materializer) { + EnableParserVars(); + m_parser_vars->m_exe_ctx = exe_ctx; + + Target *target = exe_ctx.GetTargetPtr(); + if (exe_ctx.GetFramePtr()) + m_parser_vars->m_sym_ctx = + exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything); + else if (exe_ctx.GetThreadPtr() && + exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)) + m_parser_vars->m_sym_ctx = + exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext( + lldb::eSymbolContextEverything); + else if (exe_ctx.GetProcessPtr()) { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } else if (target) { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } + + if (target) { + m_parser_vars->m_persistent_vars = llvm::cast<ClangPersistentVariables>( + target->GetPersistentExpressionStateForLanguage(eLanguageTypeC)); + + if (!ClangASTContext::GetScratch(*target)) + return false; + } + + m_parser_vars->m_target_info = GetTargetInfo(); + m_parser_vars->m_materializer = materializer; + + return true; +} + +void ClangExpressionDeclMap::InstallCodeGenerator( + clang::ASTConsumer *code_gen) { + assert(m_parser_vars); + m_parser_vars->m_code_gen = code_gen; +} + +void ClangExpressionDeclMap::DidParse() { + if (m_parser_vars && m_parser_vars->m_persistent_vars) { + for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); + entity_index < num_entities; ++entity_index) { + ExpressionVariableSP var_sp( + m_found_entities.GetVariableAtIndex(entity_index)); + if (var_sp) + llvm::cast<ClangExpressionVariable>(var_sp.get()) + ->DisableParserVars(GetParserID()); + } + + for (size_t pvar_index = 0, + num_pvars = m_parser_vars->m_persistent_vars->GetSize(); + pvar_index < num_pvars; ++pvar_index) { + ExpressionVariableSP pvar_sp( + m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); + if (ClangExpressionVariable *clang_var = + llvm::dyn_cast<ClangExpressionVariable>(pvar_sp.get())) + clang_var->DisableParserVars(GetParserID()); + } + + DisableParserVars(); + } +} + +// Interface for IRForTarget + +ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() { + assert(m_parser_vars.get()); + + TargetInfo ret; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + + Process *process = exe_ctx.GetProcessPtr(); + if (process) { + ret.byte_order = process->GetByteOrder(); + ret.address_byte_size = process->GetAddressByteSize(); + } else { + Target *target = exe_ctx.GetTargetPtr(); + if (target) { + ret.byte_order = target->GetArchitecture().GetByteOrder(); + ret.address_byte_size = target->GetArchitecture().GetAddressByteSize(); + } + } + + return ret; +} + +TypeFromUser ClangExpressionDeclMap::DeportType(ClangASTContext &target, + ClangASTContext &source, + TypeFromParser parser_type) { + assert(&target == ClangASTContext::GetScratch(*m_target)); + assert((TypeSystem *)&source == parser_type.GetTypeSystem()); + assert(&source.getASTContext() == m_ast_context); + + if (m_ast_importer_sp) { + return TypeFromUser(m_ast_importer_sp->DeportType(target, parser_type)); + } else { + lldbassert(0 && "No mechanism for deporting a type!"); + return TypeFromUser(); + } +} + +bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl, + ConstString name, + TypeFromParser parser_type, + bool is_result, + bool is_lvalue) { + assert(m_parser_vars.get()); + + ClangASTContext *ast = + llvm::dyn_cast_or_null<ClangASTContext>(parser_type.GetTypeSystem()); + if (ast == nullptr) + return false; + + if (m_parser_vars->m_materializer && is_result) { + Status err; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == nullptr) + return false; + + auto *clang_ast_context = ClangASTContext::GetScratch(*target); + if (!clang_ast_context) + return false; + + TypeFromUser user_type = DeportType(*clang_ast_context, *ast, parser_type); + + uint32_t offset = m_parser_vars->m_materializer->AddResultVariable( + user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err); + + ClangExpressionVariable *var = new ClangExpressionVariable( + exe_ctx.GetBestExecutionContextScope(), name, user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + m_found_entities.AddNewlyConstructedVariable(var); + + var->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = + var->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + var->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var->GetJITVars(GetParserID()); + + jit_vars->m_offset = offset; + + return true; + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == nullptr) + return false; + + ClangASTContext *context = ClangASTContext::GetScratch(*target); + if (!context) + return false; + + TypeFromUser user_type = DeportType(*context, *ast, parser_type); + + if (!user_type.GetOpaqueQualType()) { + LLDB_LOGF(log, "Persistent variable's type wasn't copied successfully"); + return false; + } + + if (!m_parser_vars->m_target_info.IsValid()) + return false; + + if (!m_parser_vars->m_persistent_vars) + return false; + + ClangExpressionVariable *var = llvm::cast<ClangExpressionVariable>( + m_parser_vars->m_persistent_vars + ->CreatePersistentVariable( + exe_ctx.GetBestExecutionContextScope(), name, user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size) + .get()); + + if (!var) + return false; + + var->m_frozen_sp->SetHasCompleteType(); + + if (is_result) + var->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + else + var->m_flags |= + ClangExpressionVariable::EVKeepInTarget; // explicitly-declared + // persistent variables should + // persist + + if (is_lvalue) { + var->m_flags |= ClangExpressionVariable::EVIsProgramReference; + } else { + var->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + var->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + } + + if (m_keep_result_in_memory) { + var->m_flags |= ClangExpressionVariable::EVKeepInTarget; + } + + LLDB_LOGF(log, "Created persistent variable with flags 0x%hx", var->m_flags); + + var->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = + var->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + return true; +} + +bool ClangExpressionDeclMap::AddValueToStruct(const NamedDecl *decl, + ConstString name, + llvm::Value *value, size_t size, + lldb::offset_t alignment) { + assert(m_struct_vars.get()); + assert(m_parser_vars.get()); + + bool is_persistent_variable = false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + m_struct_vars->m_struct_laid_out = false; + + if (ClangExpressionVariable::FindVariableInList(m_struct_members, decl, + GetParserID())) + return true; + + ClangExpressionVariable *var(ClangExpressionVariable::FindVariableInList( + m_found_entities, decl, GetParserID())); + + if (!var && m_parser_vars->m_persistent_vars) { + var = ClangExpressionVariable::FindVariableInList( + *m_parser_vars->m_persistent_vars, decl, GetParserID()); + is_persistent_variable = true; + } + + if (!var) + return false; + + LLDB_LOGF(log, "Adding value for (NamedDecl*)%p [%s - %s] to the structure", + static_cast<const void *>(decl), name.GetCString(), + var->GetName().GetCString()); + + // We know entity->m_parser_vars is valid because we used a parser variable + // to find it + + ClangExpressionVariable::ParserVars *parser_vars = + llvm::cast<ClangExpressionVariable>(var)->GetParserVars(GetParserID()); + + parser_vars->m_llvm_value = value; + + if (ClangExpressionVariable::JITVars *jit_vars = + llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID())) { + // We already laid this out; do not touch + + LLDB_LOGF(log, "Already placed at 0x%llx", + (unsigned long long)jit_vars->m_offset); + } + + llvm::cast<ClangExpressionVariable>(var)->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = + llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID()); + + jit_vars->m_alignment = alignment; + jit_vars->m_size = size; + + m_struct_members.AddVariable(var->shared_from_this()); + + if (m_parser_vars->m_materializer) { + uint32_t offset = 0; + + Status err; + + if (is_persistent_variable) { + ExpressionVariableSP var_sp(var->shared_from_this()); + offset = m_parser_vars->m_materializer->AddPersistentVariable( + var_sp, nullptr, err); + } else { + if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym) + offset = m_parser_vars->m_materializer->AddSymbol(*sym, err); + else if (const RegisterInfo *reg_info = var->GetRegisterInfo()) + offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err); + else if (parser_vars->m_lldb_var) + offset = m_parser_vars->m_materializer->AddVariable( + parser_vars->m_lldb_var, err); + } + + if (!err.Success()) + return false; + + LLDB_LOGF(log, "Placed at 0x%llx", (unsigned long long)offset); + + jit_vars->m_offset = + offset; // TODO DoStructLayout() should not change this. + } + + return true; +} + +bool ClangExpressionDeclMap::DoStructLayout() { + assert(m_struct_vars.get()); + + if (m_struct_vars->m_struct_laid_out) + return true; + + if (!m_parser_vars->m_materializer) + return false; + + m_struct_vars->m_struct_alignment = + m_parser_vars->m_materializer->GetStructAlignment(); + m_struct_vars->m_struct_size = + m_parser_vars->m_materializer->GetStructByteSize(); + m_struct_vars->m_struct_laid_out = true; + return true; +} + +bool ClangExpressionDeclMap::GetStructInfo(uint32_t &num_elements, size_t &size, + lldb::offset_t &alignment) { + assert(m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + num_elements = m_struct_members.GetSize(); + size = m_struct_vars->m_struct_size; + alignment = m_struct_vars->m_struct_alignment; + + return true; +} + +bool ClangExpressionDeclMap::GetStructElement(const NamedDecl *&decl, + llvm::Value *&value, + lldb::offset_t &offset, + ConstString &name, + uint32_t index) { + assert(m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + if (index >= m_struct_members.GetSize()) + return false; + + ExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); + + if (!member_sp) + return false; + + ClangExpressionVariable::ParserVars *parser_vars = + llvm::cast<ClangExpressionVariable>(member_sp.get()) + ->GetParserVars(GetParserID()); + ClangExpressionVariable::JITVars *jit_vars = + llvm::cast<ClangExpressionVariable>(member_sp.get()) + ->GetJITVars(GetParserID()); + + if (!parser_vars || !jit_vars || !member_sp->GetValueObject()) + return false; + + decl = parser_vars->m_named_decl; + value = parser_vars->m_llvm_value; + offset = jit_vars->m_offset; + name = member_sp->GetName(); + + return true; +} + +bool ClangExpressionDeclMap::GetFunctionInfo(const NamedDecl *decl, + uint64_t &ptr) { + ClangExpressionVariable *entity(ClangExpressionVariable::FindVariableInList( + m_found_entities, decl, GetParserID())); + + if (!entity) + return false; + + // We know m_parser_vars is valid since we searched for the variable by its + // NamedDecl + + ClangExpressionVariable::ParserVars *parser_vars = + entity->GetParserVars(GetParserID()); + + ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); + + return true; +} + +addr_t ClangExpressionDeclMap::GetSymbolAddress(Target &target, + Process *process, + ConstString name, + lldb::SymbolType symbol_type, + lldb_private::Module *module) { + SymbolContextList sc_list; + + if (module) + module->FindSymbolsWithNameAndType(name, symbol_type, sc_list); + else + target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list); + + const uint32_t num_matches = sc_list.GetSize(); + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + + for (uint32_t i = 0; + i < num_matches && + (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS); + i++) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(i, sym_ctx); + + const Address sym_address = sym_ctx.symbol->GetAddress(); + + if (!sym_address.IsValid()) + continue; + + switch (sym_ctx.symbol->GetType()) { + case eSymbolTypeCode: + case eSymbolTypeTrampoline: + symbol_load_addr = sym_address.GetCallableLoadAddress(&target); + break; + + case eSymbolTypeResolver: + symbol_load_addr = sym_address.GetCallableLoadAddress(&target, true); + break; + + case eSymbolTypeReExported: { + ConstString reexport_name = sym_ctx.symbol->GetReExportedSymbolName(); + if (reexport_name) { + ModuleSP reexport_module_sp; + ModuleSpec reexport_module_spec; + reexport_module_spec.GetPlatformFileSpec() = + sym_ctx.symbol->GetReExportedSymbolSharedLibrary(); + if (reexport_module_spec.GetPlatformFileSpec()) { + reexport_module_sp = + target.GetImages().FindFirstModule(reexport_module_spec); + if (!reexport_module_sp) { + reexport_module_spec.GetPlatformFileSpec().GetDirectory().Clear(); + reexport_module_sp = + target.GetImages().FindFirstModule(reexport_module_spec); + } + } + symbol_load_addr = GetSymbolAddress( + target, process, sym_ctx.symbol->GetReExportedSymbolName(), + symbol_type, reexport_module_sp.get()); + } + } break; + + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + symbol_load_addr = sym_address.GetLoadAddress(&target); + break; + } + } + + if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) { + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process); + + if (runtime) { + symbol_load_addr = runtime->LookupRuntimeSymbol(name); + } + } + + return symbol_load_addr; +} + +addr_t ClangExpressionDeclMap::GetSymbolAddress(ConstString name, + lldb::SymbolType symbol_type) { + assert(m_parser_vars.get()); + + if (!m_parser_vars->m_exe_ctx.GetTargetPtr()) + return false; + + return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), + m_parser_vars->m_exe_ctx.GetProcessPtr(), name, + symbol_type); +} + +lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable( + Target &target, ModuleSP &module, ConstString name, + CompilerDeclContext *namespace_decl) { + VariableList vars; + + if (module && namespace_decl) + module->FindGlobalVariables(name, namespace_decl, -1, vars); + else + target.GetImages().FindGlobalVariables(name, -1, vars); + + if (vars.GetSize() == 0) + return VariableSP(); + return vars.GetVariableAtIndex(0); +} + +ClangASTContext *ClangExpressionDeclMap::GetClangASTContext() { + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + if (frame == nullptr) + return nullptr; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + if (sym_ctx.block == nullptr) + return nullptr; + + CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); + if (!frame_decl_context) + return nullptr; + + return llvm::dyn_cast_or_null<ClangASTContext>( + frame_decl_context.GetTypeSystem()); +} + +// Interface for ClangASTSource + +void ClangExpressionDeclMap::FindExternalVisibleDecls( + NameSearchContext &context) { + assert(m_ast_context); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (GetImportInProgress()) { + if (log && log->GetVerbose()) + LLDB_LOGF(log, "Ignoring a query during an import"); + return; + } + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) { + if (!context.m_decl_context) + LLDB_LOGF(log, + "ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " + "'%s' in a NULL DeclContext", + current_id, name.GetCString()); + else if (const NamedDecl *context_named_decl = + dyn_cast<NamedDecl>(context.m_decl_context)) + LLDB_LOGF(log, + "ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " + "'%s' in '%s'", + current_id, name.GetCString(), + context_named_decl->getNameAsString().c_str()); + else + LLDB_LOGF(log, + "ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " + "'%s' in a '%s'", + current_id, name.GetCString(), + context.m_decl_context->getDeclKindName()); + } + + if (const NamespaceDecl *namespace_context = + dyn_cast<NamespaceDecl>(context.m_decl_context)) { + if (namespace_context->getName().str() == + std::string(g_lldb_local_vars_namespace_cstr)) { + CompilerDeclContext compiler_decl_ctx = + m_clang_ast_context->CreateDeclContext( + const_cast<clang::DeclContext *>(context.m_decl_context)); + FindExternalVisibleDecls(context, lldb::ModuleSP(), compiler_decl_ctx, + current_id); + return; + } + + ClangASTImporter::NamespaceMapSP namespace_map = + m_ast_importer_sp + ? m_ast_importer_sp->GetNamespaceMap(namespace_context) + : ClangASTImporter::NamespaceMapSP(); + + if (!namespace_map) + return; + + if (log && log->GetVerbose()) + log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", + current_id, static_cast<void *>(namespace_map.get()), + (int)namespace_map->size()); + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), + e = namespace_map->end(); + i != e; ++i) { + if (log) + log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s", + current_id, i->second.GetName().AsCString(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, i->first, i->second, current_id); + } + } else if (isa<TranslationUnitDecl>(context.m_decl_context)) { + CompilerDeclContext namespace_decl; + + if (log) + log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl, + current_id); + } + + ClangASTSource::FindExternalVisibleDecls(context); +} + +void ClangExpressionDeclMap::MaybeRegisterFunctionBody( + FunctionDecl *copied_function_decl) { + if (copied_function_decl->getBody() && m_parser_vars->m_code_gen) { + clang::DeclGroupRef decl_group_ref(copied_function_decl); + m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref); + } +} + +clang::NamedDecl *ClangExpressionDeclMap::GetPersistentDecl(ConstString name) { + if (!m_parser_vars) + return nullptr; + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + if (!target) + return nullptr; + + ClangASTContext::GetScratch(*target); + + if (!m_parser_vars->m_persistent_vars) + return nullptr; + return m_parser_vars->m_persistent_vars->GetPersistentDecl(name); +} + +void ClangExpressionDeclMap::SearchPersistenDecls(NameSearchContext &context, + const ConstString name, + unsigned int current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + NamedDecl *persistent_decl = GetPersistentDecl(name); + + if (!persistent_decl) + return; + + Decl *parser_persistent_decl = CopyDecl(persistent_decl); + + if (!parser_persistent_decl) + return; + + NamedDecl *parser_named_decl = dyn_cast<NamedDecl>(parser_persistent_decl); + + if (!parser_named_decl) + return; + + if (clang::FunctionDecl *parser_function_decl = + llvm::dyn_cast<clang::FunctionDecl>(parser_named_decl)) { + MaybeRegisterFunctionBody(parser_function_decl); + } + + LLDB_LOGF(log, " CEDM::FEVD[%u] Found persistent decl %s", current_id, + name.GetCString()); + + context.AddNamedDecl(parser_named_decl); +} + +void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context, + unsigned int current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + SymbolContext sym_ctx; + if (frame != nullptr) + sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + + if (m_ctx_obj) { + Status status; + lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status); + if (!ctx_obj_ptr || status.Fail()) + return; + + AddThisType(context, TypeFromUser(m_ctx_obj->GetCompilerType()), + current_id); + + m_struct_vars->m_object_pointer_type = + TypeFromUser(ctx_obj_ptr->GetCompilerType()); + + return; + } + + // Clang is looking for the type of "this" + + if (frame == nullptr) + return; + + // Find the block that defines the function represented by "sym_ctx" + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); + + if (!function_decl_ctx) + return; + + clang::CXXMethodDecl *method_decl = + ClangASTContext::DeclContextGetAsCXXMethodDecl(function_decl_ctx); + + if (method_decl) { + clang::CXXRecordDecl *class_decl = method_decl->getParent(); + + QualType class_qual_type(class_decl->getTypeForDecl(), 0); + + TypeFromUser class_user_type(class_qual_type.getAsOpaquePtr(), + function_decl_ctx.GetTypeSystem()); + + LLDB_LOG(log, " CEDM::FEVD[{0}] Adding type for $__lldb_class: {1}", + current_id, class_qual_type.getAsString()); + + AddThisType(context, class_user_type, current_id); + + if (method_decl->isInstance()) { + // self is a pointer to the object + + QualType class_pointer_type = + method_decl->getASTContext().getPointerType(class_qual_type); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + function_decl_ctx.GetTypeSystem()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + return; + } + + // This branch will get hit if we are executing code in the context of + // a function that claims to have an object pointer (through + // DW_AT_object_pointer?) but is not formally a method of the class. + // In that case, just look up the "this" variable in the current scope + // and use its type. + // FIXME: This code is formally correct, but clang doesn't currently + // emit DW_AT_object_pointer + // for C++ so it hasn't actually been tested. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); + + if (this_var && this_var->IsInScope(frame) && + this_var->LocationIsValidForFrame(frame)) { + Type *this_type = this_var->GetType(); + + if (!this_type) + return; + + TypeFromUser pointee_type = + this_type->GetForwardCompilerType().GetPointeeType(); + + LLDB_LOG(log, " FEVD[{0}] Adding type for $__lldb_class: {1}", current_id, + ClangUtil::GetQualType(pointee_type).getAsString()); + + AddThisType(context, pointee_type, current_id); + TypeFromUser this_user_type(this_type->GetFullCompilerType()); + m_struct_vars->m_object_pointer_type = this_user_type; + } +} + +void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context, + unsigned int current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + + if (m_ctx_obj) { + Status status; + lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status); + if (!ctx_obj_ptr || status.Fail()) + return; + + AddOneType(context, TypeFromUser(m_ctx_obj->GetCompilerType()), current_id); + + m_struct_vars->m_object_pointer_type = + TypeFromUser(ctx_obj_ptr->GetCompilerType()); + + return; + } + + // Clang is looking for the type of "*self" + + if (!frame) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + + // Find the block that defines the function represented by "sym_ctx" + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); + + if (!function_decl_ctx) + return; + + clang::ObjCMethodDecl *method_decl = + ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx); + + if (method_decl) { + ObjCInterfaceDecl *self_interface = method_decl->getClassInterface(); + + if (!self_interface) + return; + + const clang::Type *interface_type = self_interface->getTypeForDecl(); + + if (!interface_type) + return; // This is unlikely, but we have seen crashes where this + // occurred + + TypeFromUser class_user_type(QualType(interface_type, 0).getAsOpaquePtr(), + function_decl_ctx.GetTypeSystem()); + + LLDB_LOG(log, " FEVD[{0}] Adding type for $__lldb_objc_class: {1}", + current_id, ClangUtil::ToString(interface_type)); + + AddOneType(context, class_user_type, current_id); + + if (method_decl->isInstanceMethod()) { + // self is a pointer to the object + + QualType class_pointer_type = + method_decl->getASTContext().getObjCObjectPointerType( + QualType(interface_type, 0)); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + function_decl_ctx.GetTypeSystem()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } else { + // self is a Class pointer + QualType class_type = method_decl->getASTContext().getObjCClassType(); + + TypeFromUser self_user_type(class_type.getAsOpaquePtr(), + function_decl_ctx.GetTypeSystem()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + + return; + } + // This branch will get hit if we are executing code in the context of + // a function that claims to have an object pointer (through + // DW_AT_object_pointer?) but is not formally a method of the class. + // In that case, just look up the "self" variable in the current scope + // and use its type. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); + + if (!self_var) + return; + if (!self_var->IsInScope(frame)) + return; + if (!self_var->LocationIsValidForFrame(frame)) + return; + + Type *self_type = self_var->GetType(); + + if (!self_type) + return; + + CompilerType self_clang_type = self_type->GetFullCompilerType(); + + if (ClangASTContext::IsObjCClassType(self_clang_type)) { + return; + } + if (!ClangASTContext::IsObjCObjectPointerType(self_clang_type)) + return; + self_clang_type = self_clang_type.GetPointeeType(); + + if (!self_clang_type) + return; + + LLDB_LOG(log, " FEVD[{0}] Adding type for $__lldb_objc_class: {1}", + current_id, ClangUtil::ToString(self_type->GetFullCompilerType())); + + TypeFromUser class_user_type(self_clang_type); + + AddOneType(context, class_user_type, current_id); + + TypeFromUser self_user_type(self_type->GetFullCompilerType()); + + m_struct_vars->m_object_pointer_type = self_user_type; +} + +void ClangExpressionDeclMap::LookupLocalVarNamespace( + SymbolContext &sym_ctx, NameSearchContext &name_context) { + if (sym_ctx.block == nullptr) + return; + + CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); + if (!frame_decl_context) + return; + + ClangASTContext *frame_ast = llvm::dyn_cast_or_null<ClangASTContext>( + frame_decl_context.GetTypeSystem()); + if (!frame_ast) + return; + + clang::NamespaceDecl *namespace_decl = + m_clang_ast_context->GetUniqueNamespaceDeclaration( + g_lldb_local_vars_namespace_cstr, nullptr); + if (!namespace_decl) + return; + + name_context.AddNamedDecl(namespace_decl); + clang::DeclContext *ctxt = clang::Decl::castToDeclContext(namespace_decl); + ctxt->setHasExternalVisibleStorage(true); + name_context.m_found.local_vars_nsp = true; +} + +void ClangExpressionDeclMap::LookupInModulesDeclVendor( + NameSearchContext &context, ConstString name, unsigned current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_target) + return; + + auto *modules_decl_vendor = m_target->GetClangModulesDeclVendor(); + if (!modules_decl_vendor) + return; + + bool append = false; + uint32_t max_matches = 1; + std::vector<clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls)) + return; + + assert(!decls.empty() && "FindDecls returned true but no decls?"); + clang::NamedDecl *const decl_from_modules = decls[0]; + + LLDB_LOG(log, + " CAS::FEVD[{0}] Matching decl found for " + "\"{1}\" in the modules", + current_id, name); + + clang::Decl *copied_decl = CopyDecl(decl_from_modules); + if (!copied_decl) { + LLDB_LOG(log, + " CAS::FEVD[{0}] - Couldn't export a " + "declaration from the modules", + current_id); + return; + } + + if (auto copied_function = dyn_cast<clang::FunctionDecl>(copied_decl)) { + MaybeRegisterFunctionBody(copied_function); + + context.AddNamedDecl(copied_function); + + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } else if (auto copied_var = dyn_cast<clang::VarDecl>(copied_decl)) { + context.AddNamedDecl(copied_var); + context.m_found.variable = true; + } +} + +bool ClangExpressionDeclMap::LookupLocalVariable( + NameSearchContext &context, ConstString name, unsigned current_id, + SymbolContext &sym_ctx, CompilerDeclContext &namespace_decl) { + if (sym_ctx.block == nullptr) + return false; + + CompilerDeclContext decl_context = sym_ctx.block->GetDeclContext(); + if (!decl_context) + return false; + + // Make sure that the variables are parsed so that we have the + // declarations. + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + VariableListSP vars = frame->GetInScopeVariableList(true); + for (size_t i = 0; i < vars->GetSize(); i++) + vars->GetVariableAtIndex(i)->GetDecl(); + + // Search for declarations matching the name. Do not include imported + // decls in the search if we are looking for decls in the artificial + // namespace $__lldb_local_vars. + std::vector<CompilerDecl> found_decls = + decl_context.FindDeclByName(name, namespace_decl.IsValid()); + + VariableSP var; + bool variable_found = false; + for (CompilerDecl decl : found_decls) { + for (size_t vi = 0, ve = vars->GetSize(); vi != ve; ++vi) { + VariableSP candidate_var = vars->GetVariableAtIndex(vi); + if (candidate_var->GetDecl() == decl) { + var = candidate_var; + break; + } + } + + if (var && !variable_found) { + variable_found = true; + ValueObjectSP valobj = ValueObjectVariable::Create(frame, var); + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + } + } + return variable_found; +} + +/// Structure to hold the info needed when comparing function +/// declarations. +namespace { +struct FuncDeclInfo { + ConstString m_name; + CompilerType m_copied_type; + uint32_t m_decl_lvl; + SymbolContext m_sym_ctx; +}; +} // namespace + +SymbolContextList ClangExpressionDeclMap::SearchFunctionsInSymbolContexts( + const SymbolContextList &sc_list, + const CompilerDeclContext &frame_decl_context) { + // First, symplify things by looping through the symbol contexts to + // remove unwanted functions and separate out the functions we want to + // compare and prune into a separate list. Cache the info needed about + // the function declarations in a vector for efficiency. + uint32_t num_indices = sc_list.GetSize(); + SymbolContextList sc_sym_list; + std::vector<FuncDeclInfo> decl_infos; + decl_infos.reserve(num_indices); + clang::DeclContext *frame_decl_ctx = + (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext(); + ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>( + frame_decl_context.GetTypeSystem()); + + for (uint32_t index = 0; index < num_indices; ++index) { + FuncDeclInfo fdi; + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + + // We don't know enough about symbols to compare them, but we should + // keep them in the list. + Function *function = sym_ctx.function; + if (!function) { + sc_sym_list.Append(sym_ctx); + continue; + } + // Filter out functions without declaration contexts, as well as + // class/instance methods, since they'll be skipped in the code that + // follows anyway. + CompilerDeclContext func_decl_context = function->GetDeclContext(); + if (!func_decl_context || + func_decl_context.IsClassMethod(nullptr, nullptr, nullptr)) + continue; + // We can only prune functions for which we can copy the type. + CompilerType func_clang_type = function->GetType()->GetFullCompilerType(); + CompilerType copied_func_type = GuardedCopyType(func_clang_type); + if (!copied_func_type) { + sc_sym_list.Append(sym_ctx); + continue; + } + + fdi.m_sym_ctx = sym_ctx; + fdi.m_name = function->GetName(); + fdi.m_copied_type = copied_func_type; + fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL; + if (fdi.m_copied_type && func_decl_context) { + // Call CountDeclLevels to get the number of parent scopes we have + // to look through before we find the function declaration. When + // comparing functions of the same type, the one with a lower count + // will be closer to us in the lookup scope and shadows the other. + clang::DeclContext *func_decl_ctx = + (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext(); + fdi.m_decl_lvl = ast->CountDeclLevels(frame_decl_ctx, func_decl_ctx, + &fdi.m_name, &fdi.m_copied_type); + } + decl_infos.emplace_back(fdi); + } + + // Loop through the functions in our cache looking for matching types, + // then compare their scope levels to see which is closer. + std::multimap<CompilerType, const FuncDeclInfo *> matches; + for (const FuncDeclInfo &fdi : decl_infos) { + const CompilerType t = fdi.m_copied_type; + auto q = matches.find(t); + if (q != matches.end()) { + if (q->second->m_decl_lvl > fdi.m_decl_lvl) + // This function is closer; remove the old set. + matches.erase(t); + else if (q->second->m_decl_lvl < fdi.m_decl_lvl) + // The functions in our set are closer - skip this one. + continue; + } + matches.insert(std::make_pair(t, &fdi)); + } + + // Loop through our matches and add their symbol contexts to our list. + SymbolContextList sc_func_list; + for (const auto &q : matches) + sc_func_list.Append(q.second->m_sym_ctx); + + // Rejoin the lists with the functions in front. + sc_func_list.Append(sc_sym_list); + return sc_func_list; +} + +void ClangExpressionDeclMap::LookupFunction(NameSearchContext &context, + lldb::ModuleSP module_sp, + ConstString name, + CompilerDeclContext &namespace_decl, + unsigned current_id) { + if (!m_parser_vars) + return; + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + std::vector<clang::NamedDecl *> decls_from_modules; + + if (target) { + if (ClangModulesDeclVendor *decl_vendor = + target->GetClangModulesDeclVendor()) { + decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules); + } + } + + const bool include_inlines = false; + SymbolContextList sc_list; + if (namespace_decl && module_sp) { + const bool include_symbols = false; + + module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase, + include_symbols, include_inlines, sc_list); + } else if (target && !namespace_decl) { + const bool include_symbols = true; + + // TODO Fix FindFunctions so that it doesn't return + // instance methods for eFunctionNameTypeBase. + + target->GetImages().FindFunctions( + name, eFunctionNameTypeFull | eFunctionNameTypeBase, include_symbols, + include_inlines, sc_list); + } + + // If we found more than one function, see if we can use the frame's decl + // context to remove functions that are shadowed by other functions which + // match in type but are nearer in scope. + // + // AddOneFunction will not add a function whose type has already been + // added, so if there's another function in the list with a matching type, + // check to see if their decl context is a parent of the current frame's or + // was imported via a and using statement, and pick the best match + // according to lookup rules. + if (sc_list.GetSize() > 1) { + // Collect some info about our frame's context. + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + SymbolContext frame_sym_ctx; + if (frame != nullptr) + frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + CompilerDeclContext frame_decl_context = + frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext() + : CompilerDeclContext(); + + // We can't do this without a compiler decl context for our frame. + if (frame_decl_context) { + sc_list = SearchFunctionsInSymbolContexts(sc_list, frame_decl_context); + } + } + + if (sc_list.GetSize()) { + Symbol *extern_symbol = nullptr; + Symbol *non_extern_symbol = nullptr; + + for (uint32_t index = 0, num_indices = sc_list.GetSize(); + index < num_indices; ++index) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + + if (sym_ctx.function) { + CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); + + if (!decl_ctx) + continue; + + // Filter out class/instance methods. + if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) + continue; + + AddOneFunction(context, sym_ctx.function, nullptr, current_id); + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } else if (sym_ctx.symbol) { + if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) { + sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target); + if (sym_ctx.symbol == nullptr) + continue; + } + + if (sym_ctx.symbol->IsExternal()) + extern_symbol = sym_ctx.symbol; + else + non_extern_symbol = sym_ctx.symbol; + } + } + + if (!context.m_found.function_with_type_info) { + for (clang::NamedDecl *decl : decls_from_modules) { + if (llvm::isa<clang::FunctionDecl>(decl)) { + clang::NamedDecl *copied_decl = + llvm::cast_or_null<FunctionDecl>(CopyDecl(decl)); + if (copied_decl) { + context.AddNamedDecl(copied_decl); + context.m_found.function_with_type_info = true; + } + } + } + } + + if (!context.m_found.function_with_type_info) { + if (extern_symbol) { + AddOneFunction(context, nullptr, extern_symbol, current_id); + context.m_found.function = true; + } else if (non_extern_symbol) { + AddOneFunction(context, nullptr, non_extern_symbol, current_id); + context.m_found.function = true; + } + } + } +} + +void ClangExpressionDeclMap::FindExternalVisibleDecls( + NameSearchContext &context, lldb::ModuleSP module_sp, + CompilerDeclContext &namespace_decl, unsigned int current_id) { + assert(m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + if (IgnoreName(name, false)) + return; + + // Only look for functions by name out in our symbols if the function doesn't + // start with our phony prefix of '$' + + Target *target = nullptr; + StackFrame *frame = nullptr; + SymbolContext sym_ctx; + if (m_parser_vars) { + target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + } + if (frame != nullptr) + sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + + // Try the persistent decls, which take precedence over all else. + if (!namespace_decl) + SearchPersistenDecls(context, name, current_id); + + if (name.GetStringRef().startswith("$") && !namespace_decl) { + if (name == "$__lldb_class") { + LookUpLldbClass(context, current_id); + return; + } + + if (name == "$__lldb_objc_class") { + LookUpLldbObjCClass(context, current_id); + return; + } + if (name == g_lldb_local_vars_namespace_cstr) { + LookupLocalVarNamespace(sym_ctx, context); + return; + } + + // any other $__lldb names should be weeded out now + if (name.GetStringRef().startswith("$__lldb")) + return; + + // No ParserVars means we can't do register or variable lookup. + if (!m_parser_vars || !m_parser_vars->m_persistent_vars) + return; + + ExpressionVariableSP pvar_sp( + m_parser_vars->m_persistent_vars->GetVariable(name)); + + if (pvar_sp) { + AddOneVariable(context, pvar_sp, current_id); + return; + } + + assert(name.GetStringRef().startswith("$")); + llvm::StringRef reg_name = name.GetStringRef().substr(1); + + if (m_parser_vars->m_exe_ctx.GetRegisterContext()) { + const RegisterInfo *reg_info( + m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName( + reg_name)); + + if (reg_info) { + LLDB_LOGF(log, " CEDM::FEVD[%u] Found register %s", current_id, + reg_info->name); + + AddOneRegister(context, reg_info, current_id); + } + } + return; + } + + bool local_var_lookup = !namespace_decl || (namespace_decl.GetName() == + g_lldb_local_vars_namespace_cstr); + if (frame && local_var_lookup) + if (LookupLocalVariable(context, name, current_id, sym_ctx, namespace_decl)) + return; + + if (target) { + ValueObjectSP valobj; + VariableSP var; + var = FindGlobalVariable(*target, module_sp, name, &namespace_decl); + + if (var) { + valobj = ValueObjectVariable::Create(target, var); + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + LookupFunction(context, module_sp, name, namespace_decl, current_id); + + // Try the modules next. + if (!context.m_found.function_with_type_info) + LookupInModulesDeclVendor(context, name, current_id); + + if (target && !context.m_found.variable && !namespace_decl) { + // We couldn't find a non-symbol variable for this. Now we'll hunt for a + // generic data symbol, and -- if it is found -- treat it as a variable. + Status error; + + const Symbol *data_symbol = + m_parser_vars->m_sym_ctx.FindBestGlobalDataSymbol(name, error); + + if (!error.Success()) { + const unsigned diag_id = + m_ast_context->getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Level::Error, "%0"); + m_ast_context->getDiagnostics().Report(diag_id) << error.AsCString(); + } + + if (data_symbol) { + std::string warning("got name from symbols: "); + warning.append(name.AsCString()); + const unsigned diag_id = + m_ast_context->getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Level::Warning, "%0"); + m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str(); + AddOneGenericVariable(context, *data_symbol, current_id); + context.m_found.variable = true; + } + } +} + +bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var, + lldb_private::Value &var_location, + TypeFromUser *user_type, + TypeFromParser *parser_type) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + Type *var_type = var->GetType(); + + if (!var_type) { + if (log) + log->PutCString("Skipped a definition because it has no type"); + return false; + } + + CompilerType var_clang_type = var_type->GetFullCompilerType(); + + if (!var_clang_type) { + if (log) + log->PutCString("Skipped a definition because it has no Clang type"); + return false; + } + + ClangASTContext *clang_ast = llvm::dyn_cast_or_null<ClangASTContext>( + var_type->GetForwardCompilerType().GetTypeSystem()); + + if (!clang_ast) { + if (log) + log->PutCString("Skipped a definition because it has no Clang AST"); + return false; + } + + DWARFExpression &var_location_expr = var->LocationExpression(); + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + Status err; + + if (var->GetLocationIsConstantValueData()) { + DataExtractor const_value_extractor; + + if (var_location_expr.GetExpressionData(const_value_extractor)) { + var_location = Value(const_value_extractor.GetDataStart(), + const_value_extractor.GetByteSize()); + var_location.SetValueType(Value::eValueTypeHostAddress); + } else { + LLDB_LOGF(log, "Error evaluating constant variable: %s", err.AsCString()); + return false; + } + } + + CompilerType type_to_use = GuardedCopyType(var_clang_type); + + if (!type_to_use) { + LLDB_LOGF(log, + "Couldn't copy a variable's type into the parser's AST context"); + + return false; + } + + if (parser_type) + *parser_type = TypeFromParser(type_to_use); + + if (var_location.GetContextType() == Value::eContextTypeInvalid) + var_location.SetCompilerType(type_to_use); + + if (var_location.GetValueType() == Value::eValueTypeFileAddress) { + SymbolContext var_sc; + var->CalculateSymbolContext(&var_sc); + + if (!var_sc.module_sp) + return false; + + Address so_addr(var_location.GetScalar().ULongLong(), + var_sc.module_sp->GetSectionList()); + + lldb::addr_t load_addr = so_addr.GetLoadAddress(target); + + if (load_addr != LLDB_INVALID_ADDRESS) { + var_location.GetScalar() = load_addr; + var_location.SetValueType(Value::eValueTypeLoadAddress); + } + } + + if (user_type) + *user_type = TypeFromUser(var_clang_type); + + return true; +} + +void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, + VariableSP var, + ValueObjectSP valobj, + unsigned int current_id) { + assert(m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser ut; + TypeFromParser pt; + Value var_location; + + if (!GetVariableValue(var, var_location, &ut, &pt)) + return; + + clang::QualType parser_opaque_type = + QualType::getFromOpaquePtr(pt.GetOpaqueQualType()); + + if (parser_opaque_type.isNull()) + return; + + if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) { + if (const TagType *tag_type = dyn_cast<TagType>(parser_type)) + CompleteType(tag_type->getDecl()); + if (const ObjCObjectPointerType *objc_object_ptr_type = + dyn_cast<ObjCObjectPointerType>(parser_type)) + CompleteType(objc_object_ptr_type->getInterfaceDecl()); + } + + bool is_reference = pt.IsReferenceType(); + + NamedDecl *var_decl = nullptr; + if (is_reference) + var_decl = context.AddVarDecl(pt); + else + var_decl = context.AddVarDecl(pt.GetLValueReferenceType()); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariable *entity(new ClangExpressionVariable(valobj)); + m_found_entities.AddNewlyConstructedVariable(entity); + + assert(entity); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = + entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = pt; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = nullptr; + parser_vars->m_lldb_value = var_location; + parser_vars->m_lldb_var = var; + + if (is_reference) + entity->m_flags |= ClangExpressionVariable::EVTypeIsReference; + + LLDB_LOG(log, + " CEDM::FEVD[{0}] Found variable {1}, returned\n{2} (original {3})", + current_id, decl_name, ClangUtil::DumpDecl(var_decl), + ClangUtil::ToString(ut)); +} + +void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, + ExpressionVariableSP &pvar_sp, + unsigned int current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser user_type( + llvm::cast<ClangExpressionVariable>(pvar_sp.get())->GetTypeFromUser()); + + TypeFromParser parser_type(GuardedCopyType(user_type)); + + if (!parser_type.GetOpaqueQualType()) { + LLDB_LOGF(log, " CEDM::FEVD[%u] Couldn't import type for pvar %s", + current_id, pvar_sp->GetName().GetCString()); + return; + } + + NamedDecl *var_decl = + context.AddVarDecl(parser_type.GetLValueReferenceType()); + + llvm::cast<ClangExpressionVariable>(pvar_sp.get()) + ->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = + llvm::cast<ClangExpressionVariable>(pvar_sp.get()) + ->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = nullptr; + parser_vars->m_lldb_value.Clear(); + + LLDB_LOG(log, " CEDM::FEVD[{0}] Added pvar {1}, returned\n{2}", current_id, + pvar_sp->GetName(), ClangUtil::DumpDecl(var_decl)); +} + +void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context, + const Symbol &symbol, + unsigned int current_id) { + assert(m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (target == nullptr) + return; + + ClangASTContext *scratch_ast_context = ClangASTContext::GetScratch(*target); + if (!scratch_ast_context) + return; + + TypeFromUser user_type(scratch_ast_context->GetBasicType(eBasicTypeVoid) + .GetPointerType() + .GetLValueReferenceType()); + TypeFromParser parser_type(m_clang_ast_context->GetBasicType(eBasicTypeVoid) + .GetPointerType() + .GetLValueReferenceType()); + NamedDecl *var_decl = context.AddVarDecl(parser_type); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariable *entity(new ClangExpressionVariable( + m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), entity_name, + user_type, m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + m_found_entities.AddNewlyConstructedVariable(entity); + + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = + entity->GetParserVars(GetParserID()); + + const Address symbol_address = symbol.GetAddress(); + lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target); + + // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, + // user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetCompilerType(user_type); + parser_vars->m_lldb_value.GetScalar() = symbol_load_addr; + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = nullptr; + parser_vars->m_lldb_sym = &symbol; + + LLDB_LOG(log, " CEDM::FEVD[{0}] Found variable {1}, returned\n{2}", + current_id, decl_name, ClangUtil::DumpDecl(var_decl)); +} + +void ClangExpressionDeclMap::AddOneRegister(NameSearchContext &context, + const RegisterInfo *reg_info, + unsigned int current_id) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + CompilerType clang_type = + m_clang_ast_context->GetBuiltinTypeForEncodingAndBitSize( + reg_info->encoding, reg_info->byte_size * 8); + + if (!clang_type) { + LLDB_LOGF(log, " Tried to add a type for %s, but couldn't get one", + context.m_decl_name.getAsString().c_str()); + return; + } + + TypeFromParser parser_clang_type(clang_type); + + NamedDecl *var_decl = context.AddVarDecl(parser_clang_type); + + ClangExpressionVariable *entity(new ClangExpressionVariable( + m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + m_found_entities.AddNewlyConstructedVariable(entity); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName(ConstString(decl_name.c_str())); + entity->SetRegisterInfo(reg_info); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = + entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_clang_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = nullptr; + parser_vars->m_lldb_value.Clear(); + entity->m_flags |= ClangExpressionVariable::EVBareRegister; + + LLDB_LOG(log, " CEDM::FEVD[{0}] Added register {1}, returned\n{2}", + current_id, context.m_decl_name.getAsString(), + ClangUtil::DumpDecl(var_decl)); +} + +void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context, + Function *function, Symbol *symbol, + unsigned int current_id) { + assert(m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + NamedDecl *function_decl = nullptr; + Address fun_address; + CompilerType function_clang_type; + + bool is_indirect_function = false; + + if (function) { + Type *function_type = function->GetType(); + + const auto lang = function->GetCompileUnit()->GetLanguage(); + const auto name = function->GetMangled().GetMangledName().AsCString(); + const bool extern_c = (Language::LanguageIsC(lang) && + !CPlusPlusLanguage::IsCPPMangledName(name)) || + (Language::LanguageIsObjC(lang) && + !Language::LanguageIsCPlusPlus(lang)); + + if (!extern_c) { + TypeSystem *type_system = function->GetDeclContext().GetTypeSystem(); + if (llvm::isa<ClangASTContext>(type_system)) { + clang::DeclContext *src_decl_context = + (clang::DeclContext *)function->GetDeclContext() + .GetOpaqueDeclContext(); + clang::FunctionDecl *src_function_decl = + llvm::dyn_cast_or_null<clang::FunctionDecl>(src_decl_context); + if (src_function_decl && + src_function_decl->getTemplateSpecializationInfo()) { + clang::FunctionTemplateDecl *function_template = + src_function_decl->getTemplateSpecializationInfo()->getTemplate(); + clang::FunctionTemplateDecl *copied_function_template = + llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>( + CopyDecl(function_template)); + if (copied_function_template) { + if (log) { + StreamString ss; + + function->DumpSymbolContext(&ss); + + LLDB_LOG(log, + " CEDM::FEVD[{0}] Imported decl for function template" + " {1} (description {2}), returned\n{3}", + current_id, copied_function_template->getNameAsString(), + ss.GetData(), + ClangUtil::DumpDecl(copied_function_template)); + } + + context.AddNamedDecl(copied_function_template); + } + } else if (src_function_decl) { + if (clang::FunctionDecl *copied_function_decl = + llvm::dyn_cast_or_null<clang::FunctionDecl>( + CopyDecl(src_function_decl))) { + if (log) { + StreamString ss; + + function->DumpSymbolContext(&ss); + + LLDB_LOG(log, + " CEDM::FEVD[{0}]] Imported decl for function {1} " + "(description {2}), returned\n{3}", + current_id, copied_function_decl->getNameAsString(), + ss.GetData(), ClangUtil::DumpDecl(copied_function_decl)); + } + + context.AddNamedDecl(copied_function_decl); + return; + } else { + if (log) { + LLDB_LOGF(log, " Failed to import the function decl for '%s'", + src_function_decl->getName().str().c_str()); + } + } + } + } + } + + if (!function_type) { + if (log) + log->PutCString(" Skipped a function because it has no type"); + return; + } + + function_clang_type = function_type->GetFullCompilerType(); + + if (!function_clang_type) { + if (log) + log->PutCString(" Skipped a function because it has no Clang type"); + return; + } + + fun_address = function->GetAddressRange().GetBaseAddress(); + + CompilerType copied_function_type = GuardedCopyType(function_clang_type); + if (copied_function_type) { + function_decl = context.AddFunDecl(copied_function_type, extern_c); + + if (!function_decl) { + if (log) { + LLDB_LOGF( + log, + " Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}", + function_type->GetName().GetCString(), function_type->GetID()); + } + + return; + } + } else { + // We failed to copy the type we found + if (log) { + LLDB_LOGF(log, + " Failed to import the function type '%s' {0x%8.8" PRIx64 + "} into the expression parser AST contenxt", + function_type->GetName().GetCString(), + function_type->GetID()); + } + + return; + } + } else if (symbol) { + fun_address = symbol->GetAddress(); + function_decl = context.AddGenericFunDecl(); + is_indirect_function = symbol->IsIndirect(); + } else { + if (log) + log->PutCString(" AddOneFunction called with no function and no symbol"); + return; + } + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + lldb::addr_t load_addr = + fun_address.GetCallableLoadAddress(target, is_indirect_function); + + ClangExpressionVariable *entity(new ClangExpressionVariable( + m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + m_found_entities.AddNewlyConstructedVariable(entity); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName(ConstString(decl_name.c_str())); + entity->SetCompilerType(function_clang_type); + entity->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = + entity->GetParserVars(GetParserID()); + + if (load_addr != LLDB_INVALID_ADDRESS) { + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + parser_vars->m_lldb_value.GetScalar() = load_addr; + } else { + // We have to try finding a file address. + + lldb::addr_t file_addr = fun_address.GetFileAddress(); + + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress); + parser_vars->m_lldb_value.GetScalar() = file_addr; + } + + parser_vars->m_named_decl = function_decl; + parser_vars->m_llvm_value = nullptr; + + if (log) { + StreamString ss; + + fun_address.Dump(&ss, + m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription); + + LLDB_LOG(log, + " CEDM::FEVD[{0}] Found {1} function {2} (description {3}), " + "returned\n{4}", + current_id, (function ? "specific" : "generic"), decl_name, + ss.GetData(), ClangUtil::DumpDecl(function_decl)); + } +} + +void ClangExpressionDeclMap::AddThisType(NameSearchContext &context, + const TypeFromUser &ut, + unsigned int current_id) { + CompilerType copied_clang_type = GuardedCopyType(ut); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!copied_clang_type) { + if (log) + LLDB_LOGF( + log, + "ClangExpressionDeclMap::AddThisType - Couldn't import the type"); + + return; + } + + if (copied_clang_type.IsAggregateType() && + copied_clang_type.GetCompleteType()) { + CompilerType void_clang_type = + m_clang_ast_context->GetBasicType(eBasicTypeVoid); + CompilerType void_ptr_clang_type = void_clang_type.GetPointerType(); + + CompilerType method_type = m_clang_ast_context->CreateFunctionType( + void_clang_type, &void_ptr_clang_type, 1, false, 0); + + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = false; + const bool is_attr_used = true; + const bool is_artificial = false; + + CXXMethodDecl *method_decl = m_clang_ast_context->AddMethodToCXXRecordType( + copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr, + method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, + is_explicit, is_attr_used, is_artificial); + + LLDB_LOG(log, + " CEDM::AddThisType Added function $__lldb_expr " + "(description {0}) for this type\n{1}", + ClangUtil::ToString(copied_clang_type), + ClangUtil::DumpDecl(method_decl)); + } + + if (!copied_clang_type.IsValid()) + return; + + TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo( + QualType::getFromOpaquePtr(copied_clang_type.GetOpaqueQualType())); + + if (!type_source_info) + return; + + // Construct a typedef type because if "*this" is a templated type we can't + // just return ClassTemplateSpecializationDecls in response to name queries. + // Using a typedef makes this much more robust. + + TypedefDecl *typedef_decl = TypedefDecl::Create( + *m_ast_context, m_ast_context->getTranslationUnitDecl(), SourceLocation(), + SourceLocation(), context.m_decl_name.getAsIdentifierInfo(), + type_source_info); + + if (!typedef_decl) + return; + + context.AddNamedDecl(typedef_decl); + + return; +} + +void ClangExpressionDeclMap::AddOneType(NameSearchContext &context, + const TypeFromUser &ut, + unsigned int current_id) { + CompilerType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + LLDB_LOGF( + log, "ClangExpressionDeclMap::AddOneType - Couldn't import the type"); + + return; + } + + context.AddTypeDecl(copied_clang_type); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h new file mode 100644 index 00000000000..722f5e15a2a --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -0,0 +1,660 @@ +//===-- ClangExpressionDeclMap.h --------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangExpressionDeclMap_h_ +#define liblldb_ClangExpressionDeclMap_h_ + +#include <signal.h> +#include <stdint.h> + +#include <vector> + +#include "ClangASTSource.h" +#include "ClangExpressionVariable.h" + +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/lldb-public.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/DenseMap.h" + +namespace lldb_private { + +/// \class ClangExpressionDeclMap ClangExpressionDeclMap.h +/// "lldb/Expression/ClangExpressionDeclMap.h" Manages named entities that are +/// defined in LLDB's debug information. +/// +/// The Clang parser uses the ClangASTSource as an interface to request named +/// entities from outside an expression. The ClangASTSource reports back, +/// listing all possible objects corresponding to a particular name. But it +/// in turn relies on ClangExpressionDeclMap, which performs several important +/// functions. +/// +/// First, it records what variables and functions were looked up and what +/// Decls were returned for them. +/// +/// Second, it constructs a struct on behalf of IRForTarget, recording which +/// variables should be placed where and relaying this information back so +/// that IRForTarget can generate context-independent code. +/// +/// Third, it "materializes" this struct on behalf of the expression command, +/// finding the current values of each variable and placing them into the +/// struct so that it can be passed to the JITted version of the IR. +/// +/// Fourth and finally, it "dematerializes" the struct after the JITted code +/// has has executed, placing the new values back where it found the old ones. +class ClangExpressionDeclMap : public ClangASTSource { +public: + /// Constructor + /// + /// Initializes class variables. + /// + /// \param[in] keep_result_in_memory + /// If true, inhibits the normal deallocation of the memory for + /// the result persistent variable, and instead marks the variable + /// as persisting. + /// + /// \param[in] result_delegate + /// If non-NULL, use this delegate to report result values. This + /// allows the client ClangUserExpression to report a result. + /// + /// \param[in] target + /// The target to use when parsing. + /// + /// \param[in] importer + /// The ClangASTImporter to use when parsing. + /// + /// \param[in] ctx_obj + /// If not empty, then expression is evaluated in context of this object. + /// See the comment to `UserExpression::Evaluate` for details. + ClangExpressionDeclMap( + bool keep_result_in_memory, + Materializer::PersistentVariableDelegate *result_delegate, + const lldb::TargetSP &target, const lldb::ClangASTImporterSP &importer, + ValueObject *ctx_obj); + + /// Destructor + ~ClangExpressionDeclMap() override; + + /// Enable the state needed for parsing and IR transformation. + /// + /// \param[in] exe_ctx + /// The execution context to use when finding types for variables. + /// Also used to find a "scratch" AST context to store result types. + /// + /// \param[in] materializer + /// If non-NULL, the materializer to populate with information about + /// the variables to use + /// + /// \return + /// True if parsing is possible; false if it is unsafe to continue. + bool WillParse(ExecutionContext &exe_ctx, Materializer *materializer); + + void InstallCodeGenerator(clang::ASTConsumer *code_gen); + + /// Disable the state needed for parsing and IR transformation. + void DidParse(); + + /// [Used by IRForTarget] Add a variable to the list of persistent + /// variables for the process. + /// + /// \param[in] decl + /// The Clang declaration for the persistent variable, used for + /// lookup during parsing. + /// + /// \param[in] name + /// The name of the persistent variable, usually $something. + /// + /// \param[in] type + /// The type of the variable, in the Clang parser's context. + /// + /// \return + /// True on success; false otherwise. + bool AddPersistentVariable(const clang::NamedDecl *decl, + ConstString name, TypeFromParser type, + bool is_result, bool is_lvalue); + + /// [Used by IRForTarget] Add a variable to the struct that needs to + /// be materialized each time the expression runs. + /// + /// \param[in] decl + /// The Clang declaration for the variable. + /// + /// \param[in] name + /// The name of the variable. + /// + /// \param[in] value + /// The LLVM IR value for this variable. + /// + /// \param[in] size + /// The size of the variable in bytes. + /// + /// \param[in] alignment + /// The required alignment of the variable in bytes. + /// + /// \return + /// True on success; false otherwise. + bool AddValueToStruct(const clang::NamedDecl *decl, ConstString name, + llvm::Value *value, size_t size, + lldb::offset_t alignment); + + /// [Used by IRForTarget] Finalize the struct, laying out the position of + /// each object in it. + /// + /// \return + /// True on success; false otherwise. + bool DoStructLayout(); + + /// [Used by IRForTarget] Get general information about the laid-out struct + /// after DoStructLayout() has been called. + /// + /// \param[out] num_elements + /// The number of elements in the struct. + /// + /// \param[out] size + /// The size of the struct, in bytes. + /// + /// \param[out] alignment + /// The alignment of the struct, in bytes. + /// + /// \return + /// True if the information could be retrieved; false otherwise. + bool GetStructInfo(uint32_t &num_elements, size_t &size, + lldb::offset_t &alignment); + + /// [Used by IRForTarget] Get specific information about one field of the + /// laid-out struct after DoStructLayout() has been called. + /// + /// \param[out] decl + /// The parsed Decl for the field, as generated by ClangASTSource + /// on ClangExpressionDeclMap's behalf. In the case of the result + /// value, this will have the name $__lldb_result even if the + /// result value ends up having the name $1. This is an + /// implementation detail of IRForTarget. + /// + /// \param[out] value + /// The IR value for the field (usually a GlobalVariable). In + /// the case of the result value, this will have the correct + /// name ($1, for instance). This is an implementation detail + /// of IRForTarget. + /// + /// \param[out] offset + /// The offset of the field from the beginning of the struct. + /// As long as the struct is aligned according to its required + /// alignment, this offset will align the field correctly. + /// + /// \param[out] name + /// The name of the field as used in materialization. + /// + /// \param[in] index + /// The index of the field about which information is requested. + /// + /// \return + /// True if the information could be retrieved; false otherwise. + bool GetStructElement(const clang::NamedDecl *&decl, llvm::Value *&value, + lldb::offset_t &offset, ConstString &name, + uint32_t index); + + /// [Used by IRForTarget] Get information about a function given its Decl. + /// + /// \param[in] decl + /// The parsed Decl for the Function, as generated by ClangASTSource + /// on ClangExpressionDeclMap's behalf. + /// + /// \param[out] ptr + /// The absolute address of the function in the target. + /// + /// \return + /// True if the information could be retrieved; false otherwise. + bool GetFunctionInfo(const clang::NamedDecl *decl, uint64_t &ptr); + + /// [Used by IRForTarget] Get the address of a symbol given nothing but its + /// name. + /// + /// \param[in] target + /// The target to find the symbol in. If not provided, + /// then the current parsing context's Target. + /// + /// \param[in] process + /// The process to use. For Objective-C symbols, the process's + /// Objective-C language runtime may be queried if the process + /// is non-NULL. + /// + /// \param[in] name + /// The name of the symbol. + /// + /// \param[in] module + /// The module to limit the search to. This can be NULL + /// + /// \return + /// Valid load address for the symbol + lldb::addr_t GetSymbolAddress(Target &target, Process *process, + ConstString name, lldb::SymbolType symbol_type, + Module *module = nullptr); + + lldb::addr_t GetSymbolAddress(ConstString name, + lldb::SymbolType symbol_type); + + struct TargetInfo { + lldb::ByteOrder byte_order; + size_t address_byte_size; + + TargetInfo() : byte_order(lldb::eByteOrderInvalid), address_byte_size(0) {} + + bool IsValid() { + return (byte_order != lldb::eByteOrderInvalid && address_byte_size != 0); + } + }; + TargetInfo GetTargetInfo(); + + /// [Used by ClangASTSource] Find all entities matching a given name, using + /// a NameSearchContext to make Decls for them. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + void FindExternalVisibleDecls(NameSearchContext &context) override; + + /// Find all entities matching a given name in a given module/namespace, + /// using a NameSearchContext to make Decls for them. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] module + /// If non-NULL, the module to query. + /// + /// \param[in] namespace_decl + /// If valid and module is non-NULL, the parent namespace. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void FindExternalVisibleDecls(NameSearchContext &context, + lldb::ModuleSP module, + CompilerDeclContext &namespace_decl, + unsigned int current_id); + +protected: + /// Retrieves the declaration with the given name from the storage of + /// persistent declarations. + /// + /// \return + /// A persistent decl with the given name or a nullptr. + virtual clang::NamedDecl *GetPersistentDecl(ConstString name); + +private: + ExpressionVariableList + m_found_entities; ///< All entities that were looked up for the parser. + ExpressionVariableList + m_struct_members; ///< All entities that need to be placed in the struct. + bool m_keep_result_in_memory; ///< True if result persistent variables + ///generated by this expression should stay in + ///memory. + Materializer::PersistentVariableDelegate + *m_result_delegate; ///< If non-NULL, used to report expression results to + ///ClangUserExpression. + ValueObject *m_ctx_obj; ///< If not empty, then expression is + ///evaluated in context of this object. + ///For details see the comment to + ///`UserExpression::Evaluate`. + + /// The following values should not live beyond parsing + class ParserVars { + public: + ParserVars() {} + + Target *GetTarget() { + if (m_exe_ctx.GetTargetPtr()) + return m_exe_ctx.GetTargetPtr(); + else if (m_sym_ctx.target_sp) + return m_sym_ctx.target_sp.get(); + return nullptr; + } + + ExecutionContext m_exe_ctx; ///< The execution context to use when parsing. + SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables + ///and types. + ClangPersistentVariables *m_persistent_vars = + nullptr; ///< The persistent variables for the process. + bool m_enable_lookups = false; ///< Set to true during parsing if we have + ///found the first "$__lldb" name. + TargetInfo m_target_info; ///< Basic information about the target. + Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer + ///to use when reporting used + ///variables. + clang::ASTConsumer *m_code_gen = nullptr; ///< If non-NULL, a code generator + ///that receives new top-level + ///functions. + private: + DISALLOW_COPY_AND_ASSIGN(ParserVars); + }; + + std::unique_ptr<ParserVars> m_parser_vars; + + /// Activate parser-specific variables + void EnableParserVars() { + if (!m_parser_vars.get()) + m_parser_vars = std::make_unique<ParserVars>(); + } + + /// Deallocate parser-specific variables + void DisableParserVars() { m_parser_vars.reset(); } + + /// The following values contain layout information for the materialized + /// struct, but are not specific to a single materialization + struct StructVars { + StructVars() + : m_struct_alignment(0), m_struct_size(0), m_struct_laid_out(false), + m_result_name(), m_object_pointer_type(nullptr, nullptr) {} + + lldb::offset_t + m_struct_alignment; ///< The alignment of the struct in bytes. + size_t m_struct_size; ///< The size of the struct in bytes. + bool m_struct_laid_out; ///< True if the struct has been laid out and the + ///layout is valid (that is, no new fields have been + ///added since). + ConstString + m_result_name; ///< The name of the result variable ($1, for example) + TypeFromUser m_object_pointer_type; ///< The type of the "this" variable, if + ///one exists + }; + + std::unique_ptr<StructVars> m_struct_vars; + + /// Activate struct variables + void EnableStructVars() { + if (!m_struct_vars.get()) + m_struct_vars.reset(new struct StructVars); + } + + /// Deallocate struct variables + void DisableStructVars() { m_struct_vars.reset(); } + + /// Get this parser's ID for use in extracting parser- and JIT-specific data + /// from persistent variables. + uint64_t GetParserID() { return (uint64_t) this; } + + /// Should be called on all copied functions. + void MaybeRegisterFunctionBody(clang::FunctionDecl *copied_function_decl); + + /// Searches the persistent decls of the target for entities with the + /// given name. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] name + /// The name of the entities that need to be found. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void SearchPersistenDecls(NameSearchContext &context, const ConstString name, + unsigned int current_id); + + /// Handles looking up $__lldb_class which requires special treatment. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void LookUpLldbClass(NameSearchContext &context, unsigned int current_id); + + /// Handles looking up $__lldb_objc_class which requires special treatment. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void LookUpLldbObjCClass(NameSearchContext &context, unsigned int current_id); + + /// Handles looking up the synthetic namespace that contains our local + /// variables for the current frame. + /// + /// \param[in] sym_ctx + /// The current SymbolContext of this frame. + /// + /// \param[in] name_context + /// The NameSearchContext that can construct Decls for this name. + void LookupLocalVarNamespace(SymbolContext &sym_ctx, + NameSearchContext &name_context); + + /// Lookup entities in the ClangModulesDeclVendor. + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] name + /// The name of the entities that need to be found. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void LookupInModulesDeclVendor(NameSearchContext &context, ConstString name, + unsigned current_id); + + /// Looks up a local variable. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] name + /// The name of the entities that need to be found. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + /// + /// \param[in] sym_ctx + /// The current SymbolContext of this frame. + /// + /// \param[in] namespace_decl + /// The parent namespace if there is one. + /// + /// \return + /// True iff a local variable was found. + bool LookupLocalVariable(NameSearchContext &context, ConstString name, + unsigned current_id, SymbolContext &sym_ctx, + CompilerDeclContext &namespace_decl); + + /// Searches for functions in the given SymbolContextList. + /// + /// \param[in] sc_list + /// The SymbolContextList to search. + /// + /// \param[in] frame_decl_context + /// The current DeclContext of the current frame. + /// + /// \return + /// A SymbolContextList with any found functions in the front and + /// any unknown SymbolContexts which are not functions in the back. + /// The SymbolContexts for the functions are ordered by how close they are + /// to the DeclContext for the given frame DeclContext. + SymbolContextList SearchFunctionsInSymbolContexts( + const SymbolContextList &sc_list, + const CompilerDeclContext &frame_decl_context); + + /// Looks up a function. + /// + /// \param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// \param[in] module_sp + /// If non-NULL, the module to query. + /// + /// \param[in] name + /// The name of the function that should be find. + /// + /// \param[in] namespace_decl + /// If valid and module is non-NULL, the parent namespace. + /// + /// \param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + void LookupFunction(NameSearchContext &context, lldb::ModuleSP module_sp, + ConstString name, CompilerDeclContext &namespace_decl, + unsigned current_id); + + /// Given a target, find a variable that matches the given name and type. + /// + /// \param[in] target + /// The target to use as a basis for finding the variable. + /// + /// \param[in] module + /// If non-NULL, the module to search. + /// + /// \param[in] name + /// The name as a plain C string. + /// + /// \param[in] namespace_decl + /// If non-NULL and module is non-NULL, the parent namespace. + /// + /// \return + /// The LLDB Variable found, or NULL if none was found. + lldb::VariableSP FindGlobalVariable(Target &target, lldb::ModuleSP &module, + ConstString name, + CompilerDeclContext *namespace_decl); + + /// Get the value of a variable in a given execution context and return the + /// associated Types if needed. + /// + /// \param[in] var + /// The variable to evaluate. + /// + /// \param[out] var_location + /// The variable location value to fill in + /// + /// \param[out] found_type + /// The type of the found value, as it was found in the user process. + /// This is only useful when the variable is being inspected on behalf + /// of the parser, hence the default. + /// + /// \param[out] parser_type + /// The type of the found value, as it was copied into the parser's + /// AST context. This is only useful when the variable is being + /// inspected on behalf of the parser, hence the default. + /// + /// \return + /// Return true if the value was successfully filled in. + bool GetVariableValue(lldb::VariableSP &var, + lldb_private::Value &var_location, + TypeFromUser *found_type = nullptr, + TypeFromParser *parser_type = nullptr); + + /// Use the NameSearchContext to generate a Decl for the given LLDB + /// Variable, and put it in the Tuple list. + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] var + /// The LLDB Variable that needs a Decl. + /// + /// \param[in] valobj + /// The LLDB ValueObject for that variable. + void AddOneVariable(NameSearchContext &context, lldb::VariableSP var, + lldb::ValueObjectSP valobj, unsigned int current_id); + + /// Use the NameSearchContext to generate a Decl for the given persistent + /// variable, and put it in the list of found entities. + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] pvar_sp + /// The persistent variable that needs a Decl. + /// + /// \param[in] current_id + /// The ID of the current invocation of FindExternalVisibleDecls + /// for logging purposes. + void AddOneVariable(NameSearchContext &context, + lldb::ExpressionVariableSP &pvar_sp, + unsigned int current_id); + + /// Use the NameSearchContext to generate a Decl for the given LLDB symbol + /// (treated as a variable), and put it in the list of found entities. + void AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol, + unsigned int current_id); + + /// Use the NameSearchContext to generate a Decl for the given function. + /// (Functions are not placed in the Tuple list.) Can handle both fully + /// typed functions and generic functions. + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] fun + /// The Function that needs to be created. If non-NULL, this is + /// a fully-typed function. + /// + /// \param[in] sym + /// The Symbol that corresponds to a function that needs to be + /// created with generic type (unitptr_t foo(...)). + void AddOneFunction(NameSearchContext &context, Function *fun, Symbol *sym, + unsigned int current_id); + + /// Use the NameSearchContext to generate a Decl for the given register. + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] reg_info + /// The information corresponding to that register. + void AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info, + unsigned int current_id); + + /// Use the NameSearchContext to generate a Decl for the given type. (Types + /// are not placed in the Tuple list.) + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] type + /// The type that needs to be created. + void AddOneType(NameSearchContext &context, const TypeFromUser &type, + unsigned int current_id); + + /// Generate a Decl for "*this" and add a member function declaration to it + /// for the expression, then report it. + /// + /// \param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// \param[in] type + /// The type for *this. + void AddThisType(NameSearchContext &context, const TypeFromUser &type, + unsigned int current_id); + + /// Move a type out of the current ASTContext into another, but make sure to + /// export all components of the type also. + /// + /// \param[in] target + /// The ClangASTContext to move to. + /// \param[in] source + /// The ClangASTContext to move from. This is assumed to be going away. + /// \param[in] parser_type + /// The type as it appears in the source context. + /// + /// \return + /// Returns the moved type, or an empty type if there was a problem. + TypeFromUser DeportType(ClangASTContext &target, ClangASTContext &source, + TypeFromParser parser_type); + + ClangASTContext *GetClangASTContext(); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionDeclMap_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h new file mode 100644 index 00000000000..48da5abb912 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h @@ -0,0 +1,60 @@ +//===-- ClangExpressionHelper.h ---------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangExpression_h_ +#define liblldb_ClangExpression_h_ + +#include <map> +#include <string> +#include <vector> + + +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ExpressionTypeSystemHelper.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RecordingMemoryManager; + +// ClangExpressionHelper +class ClangExpressionHelper : public ExpressionTypeSystemHelper { +public: + static bool classof(const ExpressionTypeSystemHelper *ts) { + return ts->getKind() == eKindClangHelper; + } + + ClangExpressionHelper() + : ExpressionTypeSystemHelper( + ExpressionTypeSystemHelper::LLVMCastKind::eKindClangHelper) {} + + /// Destructor + virtual ~ClangExpressionHelper() {} + + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + virtual ClangExpressionDeclMap *DeclMap() = 0; + + /// Return the object that the parser should allow to access ASTs. + /// May be NULL if the ASTs do not need to be transformed. + /// + /// \param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + virtual clang::ASTConsumer * + ASTTransformer(clang::ASTConsumer *passthrough) = 0; + + virtual void CommitPersistentDecls() {} + +protected: +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpression_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp new file mode 100644 index 00000000000..8abd1494288 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -0,0 +1,1381 @@ +//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// +// +// 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 "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaConsumer.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/TargetSelect.h" + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Signals.h" + +#include "ClangDiagnostic.h" +#include "ClangExpressionParser.h" +#include "ClangUserExpression.h" + +#include "ASTUtils.h" +#include "ClangASTSource.h" +#include "ClangDiagnostic.h" +#include "ClangExpressionDeclMap.h" +#include "ClangExpressionHelper.h" +#include "ClangExpressionParser.h" +#include "ClangHost.h" +#include "ClangModulesDeclVendor.h" +#include "ClangPersistentVariables.h" +#include "IRDynamicChecks.h" +#include "IRForTarget.h" +#include "ModuleDependencyCollector.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Host/File.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringList.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +#include <cctype> +#include <memory> + +using namespace clang; +using namespace llvm; +using namespace lldb_private; + +//===----------------------------------------------------------------------===// +// Utility Methods for Clang +//===----------------------------------------------------------------------===// + +class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks { + ClangModulesDeclVendor &m_decl_vendor; + ClangPersistentVariables &m_persistent_vars; + clang::SourceManager &m_source_mgr; + StreamString m_error_stream; + bool m_has_errors = false; + +public: + LLDBPreprocessorCallbacks(ClangModulesDeclVendor &decl_vendor, + ClangPersistentVariables &persistent_vars, + clang::SourceManager &source_mgr) + : m_decl_vendor(decl_vendor), m_persistent_vars(persistent_vars), + m_source_mgr(source_mgr) {} + + void moduleImport(SourceLocation import_location, clang::ModuleIdPath path, + const clang::Module * /*null*/) override { + // Ignore modules that are imported in the wrapper code as these are not + // loaded by the user. + llvm::StringRef filename = + m_source_mgr.getPresumedLoc(import_location).getFilename(); + if (filename == ClangExpressionSourceCode::g_prefix_file_name) + return; + + SourceModule module; + + for (const std::pair<IdentifierInfo *, SourceLocation> &component : path) + module.path.push_back(ConstString(component.first->getName())); + + StreamString error_stream; + + ClangModulesDeclVendor::ModuleVector exported_modules; + if (!m_decl_vendor.AddModule(module, &exported_modules, m_error_stream)) + m_has_errors = true; + + for (ClangModulesDeclVendor::ModuleID module : exported_modules) + m_persistent_vars.AddHandLoadedClangModule(module); + } + + bool hasErrors() { return m_has_errors; } + + llvm::StringRef getErrorString() { return m_error_stream.GetString(); } +}; + +class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer { +public: + ClangDiagnosticManagerAdapter(DiagnosticOptions &opts) { + DiagnosticOptions *m_options = new DiagnosticOptions(opts); + m_options->ShowPresumedLoc = true; + m_options->ShowLevel = false; + m_os.reset(new llvm::raw_string_ostream(m_output)); + m_passthrough.reset( + new clang::TextDiagnosticPrinter(*m_os, m_options, false)); + } + + void ResetManager(DiagnosticManager *manager = nullptr) { + m_manager = manager; + } + + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const clang::Diagnostic &Info) override { + if (!m_manager) { + // We have no DiagnosticManager before/after parsing but we still could + // receive diagnostics (e.g., by the ASTImporter failing to copy decls + // when we move the expression result ot the ScratchASTContext). Let's at + // least log these diagnostics until we find a way to properly render + // them and display them to the user. + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + if (log) { + llvm::SmallVector<char, 32> diag_str; + Info.FormatDiagnostic(diag_str); + diag_str.push_back('\0'); + const char *plain_diag = diag_str.data(); + LLDB_LOG(log, "Received diagnostic outside parsing: {0}", plain_diag); + } + return; + } + + // Render diagnostic message to m_output. + m_output.clear(); + m_passthrough->HandleDiagnostic(DiagLevel, Info); + m_os->flush(); + + lldb_private::DiagnosticSeverity severity; + bool make_new_diagnostic = true; + + switch (DiagLevel) { + case DiagnosticsEngine::Level::Fatal: + case DiagnosticsEngine::Level::Error: + severity = eDiagnosticSeverityError; + break; + case DiagnosticsEngine::Level::Warning: + severity = eDiagnosticSeverityWarning; + break; + case DiagnosticsEngine::Level::Remark: + case DiagnosticsEngine::Level::Ignored: + severity = eDiagnosticSeverityRemark; + break; + case DiagnosticsEngine::Level::Note: + m_manager->AppendMessageToDiagnostic(m_output); + make_new_diagnostic = false; + } + if (make_new_diagnostic) { + // ClangDiagnostic messages are expected to have no whitespace/newlines + // around them. + std::string stripped_output = llvm::StringRef(m_output).trim(); + + auto new_diagnostic = std::make_unique<ClangDiagnostic>( + stripped_output, severity, Info.getID()); + + // Don't store away warning fixits, since the compiler doesn't have + // enough context in an expression for the warning to be useful. + // FIXME: Should we try to filter out FixIts that apply to our generated + // code, and not the user's expression? + if (severity == eDiagnosticSeverityError) { + size_t num_fixit_hints = Info.getNumFixItHints(); + for (size_t i = 0; i < num_fixit_hints; i++) { + const clang::FixItHint &fixit = Info.getFixItHint(i); + if (!fixit.isNull()) + new_diagnostic->AddFixitHint(fixit); + } + } + + m_manager->AddDiagnostic(std::move(new_diagnostic)); + } + } + + clang::TextDiagnosticPrinter *GetPassthrough() { return m_passthrough.get(); } + +private: + DiagnosticManager *m_manager = nullptr; + std::shared_ptr<clang::TextDiagnosticPrinter> m_passthrough; + /// Output stream of m_passthrough. + std::shared_ptr<llvm::raw_string_ostream> m_os; + /// Output string filled by m_os. + std::string m_output; +}; + +static void SetupModuleHeaderPaths(CompilerInstance *compiler, + std::vector<std::string> include_directories, + lldb::TargetSP target_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + HeaderSearchOptions &search_opts = compiler->getHeaderSearchOpts(); + + for (const std::string &dir : include_directories) { + search_opts.AddPath(dir, frontend::System, false, true); + LLDB_LOG(log, "Added user include dir: {0}", dir); + } + + llvm::SmallString<128> module_cache; + auto props = ModuleList::GetGlobalModuleListProperties(); + props.GetClangModulesCachePath().GetPath(module_cache); + search_opts.ModuleCachePath = module_cache.str(); + LLDB_LOG(log, "Using module cache path: {0}", module_cache.c_str()); + + search_opts.ResourceDir = GetClangResourceDir().GetPath(); + + search_opts.ImplicitModuleMaps = true; +} + +//===----------------------------------------------------------------------===// +// Implementation of ClangExpressionParser +//===----------------------------------------------------------------------===// + +ClangExpressionParser::ClangExpressionParser( + ExecutionContextScope *exe_scope, Expression &expr, + bool generate_debug_info, std::vector<std::string> include_directories, + std::string filename) + : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(), + m_pp_callbacks(nullptr), + m_include_directories(std::move(include_directories)), + m_filename(std::move(filename)) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + // We can't compile expressions without a target. So if the exe_scope is + // null or doesn't have a target, then we just need to get out of here. I'll + // lldb_assert and not make any of the compiler objects since + // I can't return errors directly from the constructor. Further calls will + // check if the compiler was made and + // bag out if it wasn't. + + if (!exe_scope) { + lldb_assert(exe_scope, "Can't make an expression parser with a null scope.", + __FUNCTION__, __FILE__, __LINE__); + return; + } + + lldb::TargetSP target_sp; + target_sp = exe_scope->CalculateTarget(); + if (!target_sp) { + lldb_assert(target_sp.get(), + "Can't make an expression parser with a null target.", + __FUNCTION__, __FILE__, __LINE__); + return; + } + + // 1. Create a new compiler instance. + m_compiler.reset(new CompilerInstance()); + + // When capturing a reproducer, hook up the file collector with clang to + // collector modules and headers. + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { + repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>(); + m_compiler->setModuleDepCollector( + std::make_shared<ModuleDependencyCollectorAdaptor>( + fp.GetFileCollector())); + DependencyOutputOptions &opts = m_compiler->getDependencyOutputOpts(); + opts.IncludeSystemHeaders = true; + opts.IncludeModuleFiles = true; + } + + // Make sure clang uses the same VFS as LLDB. + m_compiler->createFileManager(FileSystem::Instance().GetVirtualFileSystem()); + + lldb::LanguageType frame_lang = + expr.Language(); // defaults to lldb::eLanguageTypeUnknown + bool overridden_target_opts = false; + lldb_private::LanguageRuntime *lang_rt = nullptr; + + std::string abi; + ArchSpec target_arch; + target_arch = target_sp->GetArchitecture(); + + const auto target_machine = target_arch.GetMachine(); + + // If the expression is being evaluated in the context of an existing stack + // frame, we introspect to see if the language runtime is available. + + lldb::StackFrameSP frame_sp = exe_scope->CalculateStackFrame(); + lldb::ProcessSP process_sp = exe_scope->CalculateProcess(); + + // Make sure the user hasn't provided a preferred execution language with + // `expression --language X -- ...` + if (frame_sp && frame_lang == lldb::eLanguageTypeUnknown) + frame_lang = frame_sp->GetLanguage(); + + if (process_sp && frame_lang != lldb::eLanguageTypeUnknown) { + lang_rt = process_sp->GetLanguageRuntime(frame_lang); + LLDB_LOGF(log, "Frame has language of type %s", + Language::GetNameForLanguageType(frame_lang)); + } + + // 2. Configure the compiler with a set of default options that are + // appropriate for most situations. + if (target_arch.IsValid()) { + std::string triple = target_arch.GetTriple().str(); + m_compiler->getTargetOpts().Triple = triple; + LLDB_LOGF(log, "Using %s as the target triple", + m_compiler->getTargetOpts().Triple.c_str()); + } else { + // If we get here we don't have a valid target and just have to guess. + // Sometimes this will be ok to just use the host target triple (when we + // evaluate say "2+3", but other expressions like breakpoint conditions and + // other things that _are_ target specific really shouldn't just be using + // the host triple. In such a case the language runtime should expose an + // overridden options set (3), below. + m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); + LLDB_LOGF(log, "Using default target triple of %s", + m_compiler->getTargetOpts().Triple.c_str()); + } + // Now add some special fixes for known architectures: Any arm32 iOS + // environment, but not on arm64 + if (m_compiler->getTargetOpts().Triple.find("arm64") == std::string::npos && + m_compiler->getTargetOpts().Triple.find("arm") != std::string::npos && + m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) { + m_compiler->getTargetOpts().ABI = "apcs-gnu"; + } + // Supported subsets of x86 + if (target_machine == llvm::Triple::x86 || + target_machine == llvm::Triple::x86_64) { + m_compiler->getTargetOpts().Features.push_back("+sse"); + m_compiler->getTargetOpts().Features.push_back("+sse2"); + } + + // Set the target CPU to generate code for. This will be empty for any CPU + // that doesn't really need to make a special + // CPU string. + m_compiler->getTargetOpts().CPU = target_arch.GetClangTargetCPU(); + + // Set the target ABI + abi = GetClangTargetABI(target_arch); + if (!abi.empty()) + m_compiler->getTargetOpts().ABI = abi; + + // 3. Now allow the runtime to provide custom configuration options for the + // target. In this case, a specialized language runtime is available and we + // can query it for extra options. For 99% of use cases, this will not be + // needed and should be provided when basic platform detection is not enough. + if (lang_rt) + overridden_target_opts = + lang_rt->GetOverrideExprOptions(m_compiler->getTargetOpts()); + + if (overridden_target_opts) + if (log && log->GetVerbose()) { + LLDB_LOGV( + log, "Using overridden target options for the expression evaluation"); + + auto opts = m_compiler->getTargetOpts(); + LLDB_LOGV(log, "Triple: '{0}'", opts.Triple); + LLDB_LOGV(log, "CPU: '{0}'", opts.CPU); + LLDB_LOGV(log, "FPMath: '{0}'", opts.FPMath); + LLDB_LOGV(log, "ABI: '{0}'", opts.ABI); + LLDB_LOGV(log, "LinkerVersion: '{0}'", opts.LinkerVersion); + StringList::LogDump(log, opts.FeaturesAsWritten, "FeaturesAsWritten"); + StringList::LogDump(log, opts.Features, "Features"); + } + + // 4. Create and install the target on the compiler. + m_compiler->createDiagnostics(); + auto target_info = TargetInfo::CreateTargetInfo( + m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts); + if (log) { + LLDB_LOGF(log, "Using SIMD alignment: %d", + target_info->getSimdDefaultAlign()); + LLDB_LOGF(log, "Target datalayout string: '%s'", + target_info->getDataLayout().getStringRepresentation().c_str()); + LLDB_LOGF(log, "Target ABI: '%s'", target_info->getABI().str().c_str()); + LLDB_LOGF(log, "Target vector alignment: %d", + target_info->getMaxVectorAlign()); + } + m_compiler->setTarget(target_info); + + assert(m_compiler->hasTarget()); + + // 5. Set language options. + lldb::LanguageType language = expr.Language(); + LangOptions &lang_opts = m_compiler->getLangOpts(); + + switch (language) { + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC89: + case lldb::eLanguageTypeC99: + case lldb::eLanguageTypeC11: + // FIXME: the following language option is a temporary workaround, + // to "ask for C, get C++." + // For now, the expression parser must use C++ anytime the language is a C + // family language, because the expression parser uses features of C++ to + // capture values. + lang_opts.CPlusPlus = true; + break; + case lldb::eLanguageTypeObjC: + lang_opts.ObjC = true; + // FIXME: the following language option is a temporary workaround, + // to "ask for ObjC, get ObjC++" (see comment above). + lang_opts.CPlusPlus = true; + + // Clang now sets as default C++14 as the default standard (with + // GNU extensions), so we do the same here to avoid mismatches that + // cause compiler error when evaluating expressions (e.g. nullptr not found + // as it's a C++11 feature). Currently lldb evaluates C++14 as C++11 (see + // two lines below) so we decide to be consistent with that, but this could + // be re-evaluated in the future. + lang_opts.CPlusPlus11 = true; + break; + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + lang_opts.CPlusPlus11 = true; + m_compiler->getHeaderSearchOpts().UseLibcxx = true; + LLVM_FALLTHROUGH; + case lldb::eLanguageTypeC_plus_plus_03: + lang_opts.CPlusPlus = true; + if (process_sp) + lang_opts.ObjC = + process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC) != nullptr; + break; + case lldb::eLanguageTypeObjC_plus_plus: + case lldb::eLanguageTypeUnknown: + default: + lang_opts.ObjC = true; + lang_opts.CPlusPlus = true; + lang_opts.CPlusPlus11 = true; + m_compiler->getHeaderSearchOpts().UseLibcxx = true; + break; + } + + lang_opts.Bool = true; + lang_opts.WChar = true; + lang_opts.Blocks = true; + lang_opts.DebuggerSupport = + true; // Features specifically for debugger clients + if (expr.DesiredResultType() == Expression::eResultTypeId) + lang_opts.DebuggerCastResultToId = true; + + lang_opts.CharIsSigned = ArchSpec(m_compiler->getTargetOpts().Triple.c_str()) + .CharIsSignedByDefault(); + + // Spell checking is a nice feature, but it ends up completing a lot of types + // that we didn't strictly speaking need to complete. As a result, we spend a + // long time parsing and importing debug information. + lang_opts.SpellChecking = false; + + auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr); + if (clang_expr && clang_expr->DidImportCxxModules()) { + LLDB_LOG(log, "Adding lang options for importing C++ modules"); + + lang_opts.Modules = true; + // We want to implicitly build modules. + lang_opts.ImplicitModules = true; + // To automatically import all submodules when we import 'std'. + lang_opts.ModulesLocalVisibility = false; + + // We use the @import statements, so we need this: + // FIXME: We could use the modules-ts, but that currently doesn't work. + lang_opts.ObjC = true; + + // Options we need to parse libc++ code successfully. + // FIXME: We should ask the driver for the appropriate default flags. + lang_opts.GNUMode = true; + lang_opts.GNUKeywords = true; + lang_opts.DoubleSquareBracketAttributes = true; + lang_opts.CPlusPlus11 = true; + + // The Darwin libc expects this macro to be set. + lang_opts.GNUCVersion = 40201; + + SetupModuleHeaderPaths(m_compiler.get(), m_include_directories, + target_sp); + } + + if (process_sp && lang_opts.ObjC) { + if (auto *runtime = ObjCLanguageRuntime::Get(*process_sp)) { + if (runtime->GetRuntimeVersion() == + ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2) + lang_opts.ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); + else + lang_opts.ObjCRuntime.set(ObjCRuntime::FragileMacOSX, + VersionTuple(10, 7)); + + if (runtime->HasNewLiteralsAndIndexing()) + lang_opts.DebuggerObjCLiteral = true; + } + } + + lang_opts.ThreadsafeStatics = false; + lang_opts.AccessControl = false; // Debuggers get universal access + lang_opts.DollarIdents = true; // $ indicates a persistent variable name + // We enable all builtin functions beside the builtins from libc/libm (e.g. + // 'fopen'). Those libc functions are already correctly handled by LLDB, and + // additionally enabling them as expandable builtins is breaking Clang. + lang_opts.NoBuiltin = true; + + // Set CodeGen options + m_compiler->getCodeGenOpts().EmitDeclMetadata = true; + m_compiler->getCodeGenOpts().InstrumentFunctions = false; + m_compiler->getCodeGenOpts().setFramePointer( + CodeGenOptions::FramePointerKind::All); + if (generate_debug_info) + m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo); + else + m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo); + + // Disable some warnings. + m_compiler->getDiagnostics().setSeverityForGroup( + clang::diag::Flavor::WarningOrError, "unused-value", + clang::diag::Severity::Ignored, SourceLocation()); + m_compiler->getDiagnostics().setSeverityForGroup( + clang::diag::Flavor::WarningOrError, "odr", + clang::diag::Severity::Ignored, SourceLocation()); + + // Inform the target of the language options + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + m_compiler->getTarget().adjust(m_compiler->getLangOpts()); + + // 6. Set up the diagnostic buffer for reporting errors + + auto diag_mgr = new ClangDiagnosticManagerAdapter( + m_compiler->getDiagnostics().getDiagnosticOptions()); + m_compiler->getDiagnostics().setClient(diag_mgr); + + // 7. Set up the source management objects inside the compiler + m_compiler->createFileManager(); + if (!m_compiler->hasSourceManager()) + m_compiler->createSourceManager(m_compiler->getFileManager()); + m_compiler->createPreprocessor(TU_Complete); + + if (ClangModulesDeclVendor *decl_vendor = + target_sp->GetClangModulesDeclVendor()) { + if (auto *clang_persistent_vars = llvm::cast<ClangPersistentVariables>( + target_sp->GetPersistentExpressionStateForLanguage( + lldb::eLanguageTypeC))) { + std::unique_ptr<PPCallbacks> pp_callbacks( + new LLDBPreprocessorCallbacks(*decl_vendor, *clang_persistent_vars, + m_compiler->getSourceManager())); + m_pp_callbacks = + static_cast<LLDBPreprocessorCallbacks *>(pp_callbacks.get()); + m_compiler->getPreprocessor().addPPCallbacks(std::move(pp_callbacks)); + } + } + + // 8. Most of this we get from the CompilerInstance, but we also want to give + // the context an ExternalASTSource. + + auto &PP = m_compiler->getPreprocessor(); + auto &builtin_context = PP.getBuiltinInfo(); + builtin_context.initializeBuiltins(PP.getIdentifierTable(), + m_compiler->getLangOpts()); + + m_compiler->createASTContext(); + clang::ASTContext &ast_context = m_compiler->getASTContext(); + + m_ast_context.reset(new ClangASTContext(ast_context)); + + std::string module_name("$__lldb_module"); + + m_llvm_context.reset(new LLVMContext()); + m_code_generator.reset(CreateLLVMCodeGen( + m_compiler->getDiagnostics(), module_name, + m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(), + m_compiler->getCodeGenOpts(), *m_llvm_context)); +} + +ClangExpressionParser::~ClangExpressionParser() {} + +namespace { + +/// \class CodeComplete +/// +/// A code completion consumer for the clang Sema that is responsible for +/// creating the completion suggestions when a user requests completion +/// of an incomplete `expr` invocation. +class CodeComplete : public CodeCompleteConsumer { + CodeCompletionTUInfo m_info; + + std::string m_expr; + unsigned m_position = 0; + CompletionRequest &m_request; + /// The printing policy we use when printing declarations for our completion + /// descriptions. + clang::PrintingPolicy m_desc_policy; + + /// Returns true if the given character can be used in an identifier. + /// This also returns true for numbers because for completion we usually + /// just iterate backwards over iterators. + /// + /// Note: lldb uses '$' in its internal identifiers, so we also allow this. + static bool IsIdChar(char c) { + return c == '_' || std::isalnum(c) || c == '$'; + } + + /// Returns true if the given character is used to separate arguments + /// in the command line of lldb. + static bool IsTokenSeparator(char c) { return c == ' ' || c == '\t'; } + + /// Drops all tokens in front of the expression that are unrelated for + /// the completion of the cmd line. 'unrelated' means here that the token + /// is not interested for the lldb completion API result. + StringRef dropUnrelatedFrontTokens(StringRef cmd) { + if (cmd.empty()) + return cmd; + + // If we are at the start of a word, then all tokens are unrelated to + // the current completion logic. + if (IsTokenSeparator(cmd.back())) + return StringRef(); + + // Remove all previous tokens from the string as they are unrelated + // to completing the current token. + StringRef to_remove = cmd; + while (!to_remove.empty() && !IsTokenSeparator(to_remove.back())) { + to_remove = to_remove.drop_back(); + } + cmd = cmd.drop_front(to_remove.size()); + + return cmd; + } + + /// Removes the last identifier token from the given cmd line. + StringRef removeLastToken(StringRef cmd) { + while (!cmd.empty() && IsIdChar(cmd.back())) { + cmd = cmd.drop_back(); + } + return cmd; + } + + /// Attemps to merge the given completion from the given position into the + /// existing command. Returns the completion string that can be returned to + /// the lldb completion API. + std::string mergeCompletion(StringRef existing, unsigned pos, + StringRef completion) { + StringRef existing_command = existing.substr(0, pos); + // We rewrite the last token with the completion, so let's drop that + // token from the command. + existing_command = removeLastToken(existing_command); + // We also should remove all previous tokens from the command as they + // would otherwise be added to the completion that already has the + // completion. + existing_command = dropUnrelatedFrontTokens(existing_command); + return existing_command.str() + completion.str(); + } + +public: + /// Constructs a CodeComplete consumer that can be attached to a Sema. + /// + /// \param[out] expr + /// The whole expression string that we are currently parsing. This + /// string needs to be equal to the input the user typed, and NOT the + /// final code that Clang is parsing. + /// \param[out] position + /// The character position of the user cursor in the `expr` parameter. + /// + CodeComplete(CompletionRequest &request, clang::LangOptions ops, + std::string expr, unsigned position) + : CodeCompleteConsumer(CodeCompleteOptions()), + m_info(std::make_shared<GlobalCodeCompletionAllocator>()), m_expr(expr), + m_position(position), m_request(request), m_desc_policy(ops) { + + // Ensure that the printing policy is producing a description that is as + // short as possible. + m_desc_policy.SuppressScope = true; + m_desc_policy.SuppressTagKeyword = true; + m_desc_policy.FullyQualifiedName = false; + m_desc_policy.TerseOutput = true; + m_desc_policy.IncludeNewlines = false; + m_desc_policy.UseVoidForZeroParams = false; + m_desc_policy.Bool = true; + } + + /// Deregisters and destroys this code-completion consumer. + ~CodeComplete() override {} + + /// \name Code-completion filtering + /// Check if the result should be filtered out. + bool isResultFilteredOut(StringRef Filter, + CodeCompletionResult Result) override { + // This code is mostly copied from CodeCompleteConsumer. + switch (Result.Kind) { + case CodeCompletionResult::RK_Declaration: + return !( + Result.Declaration->getIdentifier() && + Result.Declaration->getIdentifier()->getName().startswith(Filter)); + case CodeCompletionResult::RK_Keyword: + return !StringRef(Result.Keyword).startswith(Filter); + case CodeCompletionResult::RK_Macro: + return !Result.Macro->getName().startswith(Filter); + case CodeCompletionResult::RK_Pattern: + return !StringRef(Result.Pattern->getAsString()).startswith(Filter); + } + // If we trigger this assert or the above switch yields a warning, then + // CodeCompletionResult has been enhanced with more kinds of completion + // results. Expand the switch above in this case. + assert(false && "Unknown completion result type?"); + // If we reach this, then we should just ignore whatever kind of unknown + // result we got back. We probably can't turn it into any kind of useful + // completion suggestion with the existing code. + return true; + } + + /// \name Code-completion callbacks + /// Process the finalized code-completion results. + void ProcessCodeCompleteResults(Sema &SemaRef, CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) override { + + // The Sema put the incomplete token we try to complete in here during + // lexing, so we need to retrieve it here to know what we are completing. + StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter(); + + // Iterate over all the results. Filter out results we don't want and + // process the rest. + for (unsigned I = 0; I != NumResults; ++I) { + // Filter the results with the information from the Sema. + if (!Filter.empty() && isResultFilteredOut(Filter, Results[I])) + continue; + + CodeCompletionResult &R = Results[I]; + std::string ToInsert; + std::string Description; + // Handle the different completion kinds that come from the Sema. + switch (R.Kind) { + case CodeCompletionResult::RK_Declaration: { + const NamedDecl *D = R.Declaration; + ToInsert = R.Declaration->getNameAsString(); + // If we have a function decl that has no arguments we want to + // complete the empty parantheses for the user. If the function has + // arguments, we at least complete the opening bracket. + if (const FunctionDecl *F = dyn_cast<FunctionDecl>(D)) { + if (F->getNumParams() == 0) + ToInsert += "()"; + else + ToInsert += "("; + raw_string_ostream OS(Description); + F->print(OS, m_desc_policy, false); + OS.flush(); + } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) { + Description = V->getType().getAsString(m_desc_policy); + } else if (const FieldDecl *F = dyn_cast<FieldDecl>(D)) { + Description = F->getType().getAsString(m_desc_policy); + } else if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) { + // If we try to complete a namespace, then we can directly append + // the '::'. + if (!N->isAnonymousNamespace()) + ToInsert += "::"; + } + break; + } + case CodeCompletionResult::RK_Keyword: + ToInsert = R.Keyword; + break; + case CodeCompletionResult::RK_Macro: + ToInsert = R.Macro->getName().str(); + break; + case CodeCompletionResult::RK_Pattern: + ToInsert = R.Pattern->getTypedText(); + break; + } + // At this point all information is in the ToInsert string. + + // We also filter some internal lldb identifiers here. The user + // shouldn't see these. + if (StringRef(ToInsert).startswith("$__lldb_")) + continue; + if (!ToInsert.empty()) { + // Merge the suggested Token into the existing command line to comply + // with the kind of result the lldb API expects. + std::string CompletionSuggestion = + mergeCompletion(m_expr, m_position, ToInsert); + m_request.AddCompletion(CompletionSuggestion, Description); + } + } + } + + /// \param S the semantic-analyzer object for which code-completion is being + /// done. + /// + /// \param CurrentArg the index of the current argument. + /// + /// \param Candidates an array of overload candidates. + /// + /// \param NumCandidates the number of overload candidates + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates, + SourceLocation OpenParLoc) override { + // At the moment we don't filter out any overloaded candidates. + } + + CodeCompletionAllocator &getAllocator() override { + return m_info.getAllocator(); + } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return m_info; } +}; +} // namespace + +bool ClangExpressionParser::Complete(CompletionRequest &request, unsigned line, + unsigned pos, unsigned typed_pos) { + DiagnosticManager mgr; + // We need the raw user expression here because that's what the CodeComplete + // class uses to provide completion suggestions. + // However, the `Text` method only gives us the transformed expression here. + // To actually get the raw user input here, we have to cast our expression to + // the LLVMUserExpression which exposes the right API. This should never fail + // as we always have a ClangUserExpression whenever we call this. + ClangUserExpression *llvm_expr = cast<ClangUserExpression>(&m_expr); + CodeComplete CC(request, m_compiler->getLangOpts(), llvm_expr->GetUserText(), + typed_pos); + // We don't need a code generator for parsing. + m_code_generator.reset(); + // Start parsing the expression with our custom code completion consumer. + ParseInternal(mgr, &CC, line, pos); + return true; +} + +unsigned ClangExpressionParser::Parse(DiagnosticManager &diagnostic_manager) { + return ParseInternal(diagnostic_manager); +} + +unsigned +ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, + CodeCompleteConsumer *completion_consumer, + unsigned completion_line, + unsigned completion_column) { + ClangDiagnosticManagerAdapter *adapter = + static_cast<ClangDiagnosticManagerAdapter *>( + m_compiler->getDiagnostics().getClient()); + auto diag_buf = adapter->GetPassthrough(); + + adapter->ResetManager(&diagnostic_manager); + + const char *expr_text = m_expr.Text(); + + clang::SourceManager &source_mgr = m_compiler->getSourceManager(); + bool created_main_file = false; + + // Clang wants to do completion on a real file known by Clang's file manager, + // so we have to create one to make this work. + // TODO: We probably could also simulate to Clang's file manager that there + // is a real file that contains our code. + bool should_create_file = completion_consumer != nullptr; + + // We also want a real file on disk if we generate full debug info. + should_create_file |= m_compiler->getCodeGenOpts().getDebugInfo() == + codegenoptions::FullDebugInfo; + + if (should_create_file) { + int temp_fd = -1; + llvm::SmallString<128> result_path; + if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) { + tmpdir_file_spec.AppendPathComponent("lldb-%%%%%%.expr"); + std::string temp_source_path = tmpdir_file_spec.GetPath(); + llvm::sys::fs::createUniqueFile(temp_source_path, temp_fd, result_path); + } else { + llvm::sys::fs::createTemporaryFile("lldb", "expr", temp_fd, result_path); + } + + if (temp_fd != -1) { + lldb_private::NativeFile file(temp_fd, File::eOpenOptionWrite, true); + const size_t expr_text_len = strlen(expr_text); + size_t bytes_written = expr_text_len; + if (file.Write(expr_text, bytes_written).Success()) { + if (bytes_written == expr_text_len) { + file.Close(); + if (auto fileEntry = + m_compiler->getFileManager().getFile(result_path)) { + source_mgr.setMainFileID(source_mgr.createFileID( + *fileEntry, + SourceLocation(), SrcMgr::C_User)); + created_main_file = true; + } + } + } + } + } + + if (!created_main_file) { + std::unique_ptr<MemoryBuffer> memory_buffer = + MemoryBuffer::getMemBufferCopy(expr_text, m_filename); + source_mgr.setMainFileID(source_mgr.createFileID(std::move(memory_buffer))); + } + + diag_buf->BeginSourceFile(m_compiler->getLangOpts(), + &m_compiler->getPreprocessor()); + + ClangExpressionHelper *type_system_helper = + dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper()); + + // If we want to parse for code completion, we need to attach our code + // completion consumer to the Sema and specify a completion position. + // While parsing the Sema will call this consumer with the provided + // completion suggestions. + if (completion_consumer) { + auto main_file = source_mgr.getFileEntryForID(source_mgr.getMainFileID()); + auto &PP = m_compiler->getPreprocessor(); + // Lines and columns start at 1 in Clang, but code completion positions are + // indexed from 0, so we need to add 1 to the line and column here. + ++completion_line; + ++completion_column; + PP.SetCodeCompletionPoint(main_file, completion_line, completion_column); + } + + ASTConsumer *ast_transformer = + type_system_helper->ASTTransformer(m_code_generator.get()); + + std::unique_ptr<clang::ASTConsumer> Consumer; + if (ast_transformer) { + Consumer.reset(new ASTConsumerForwarder(ast_transformer)); + } else if (m_code_generator) { + Consumer.reset(new ASTConsumerForwarder(m_code_generator.get())); + } else { + Consumer.reset(new ASTConsumer()); + } + + clang::ASTContext &ast_context = m_compiler->getASTContext(); + + m_compiler->setSema(new Sema(m_compiler->getPreprocessor(), ast_context, + *Consumer, TU_Complete, completion_consumer)); + m_compiler->setASTConsumer(std::move(Consumer)); + + if (ast_context.getLangOpts().Modules) { + m_compiler->createASTReader(); + m_ast_context->setSema(&m_compiler->getSema()); + } + + ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap(); + if (decl_map) { + decl_map->InstallCodeGenerator(&m_compiler->getASTConsumer()); + + clang::ExternalASTSource *ast_source = decl_map->CreateProxy(); + + if (ast_context.getExternalSource()) { + auto module_wrapper = + new ExternalASTSourceWrapper(ast_context.getExternalSource()); + + auto ast_source_wrapper = new ExternalASTSourceWrapper(ast_source); + + auto multiplexer = + new SemaSourceWithPriorities(*module_wrapper, *ast_source_wrapper); + IntrusiveRefCntPtr<ExternalASTSource> Source(multiplexer); + ast_context.setExternalSource(Source); + } else { + ast_context.setExternalSource(ast_source); + } + decl_map->InstallASTContext(*m_ast_context); + } + + // Check that the ASTReader is properly attached to ASTContext and Sema. + if (ast_context.getLangOpts().Modules) { + assert(m_compiler->getASTContext().getExternalSource() && + "ASTContext doesn't know about the ASTReader?"); + assert(m_compiler->getSema().getExternalSource() && + "Sema doesn't know about the ASTReader?"); + } + + { + llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema( + &m_compiler->getSema()); + ParseAST(m_compiler->getSema(), false, false); + } + + // Make sure we have no pointer to the Sema we are about to destroy. + if (ast_context.getLangOpts().Modules) + m_ast_context->setSema(nullptr); + // Destroy the Sema. This is necessary because we want to emulate the + // original behavior of ParseAST (which also destroys the Sema after parsing). + m_compiler->setSema(nullptr); + + diag_buf->EndSourceFile(); + + unsigned num_errors = diag_buf->getNumErrors(); + + if (m_pp_callbacks && m_pp_callbacks->hasErrors()) { + num_errors++; + diagnostic_manager.PutString(eDiagnosticSeverityError, + "while importing modules:"); + diagnostic_manager.AppendMessageToDiagnostic( + m_pp_callbacks->getErrorString()); + } + + if (!num_errors) { + type_system_helper->CommitPersistentDecls(); + } + + adapter->ResetManager(); + + return num_errors; +} + +std::string +ClangExpressionParser::GetClangTargetABI(const ArchSpec &target_arch) { + std::string abi; + + if (target_arch.IsMIPS()) { + switch (target_arch.GetFlags() & ArchSpec::eMIPSABI_mask) { + case ArchSpec::eMIPSABI_N64: + abi = "n64"; + break; + case ArchSpec::eMIPSABI_N32: + abi = "n32"; + break; + case ArchSpec::eMIPSABI_O32: + abi = "o32"; + break; + default: + break; + } + } + return abi; +} + +bool ClangExpressionParser::RewriteExpression( + DiagnosticManager &diagnostic_manager) { + clang::SourceManager &source_manager = m_compiler->getSourceManager(); + clang::edit::EditedSource editor(source_manager, m_compiler->getLangOpts(), + nullptr); + clang::edit::Commit commit(editor); + clang::Rewriter rewriter(source_manager, m_compiler->getLangOpts()); + + class RewritesReceiver : public edit::EditsReceiver { + Rewriter &rewrite; + + public: + RewritesReceiver(Rewriter &in_rewrite) : rewrite(in_rewrite) {} + + void insert(SourceLocation loc, StringRef text) override { + rewrite.InsertText(loc, text); + } + void replace(CharSourceRange range, StringRef text) override { + rewrite.ReplaceText(range.getBegin(), rewrite.getRangeSize(range), text); + } + }; + + RewritesReceiver rewrites_receiver(rewriter); + + const DiagnosticList &diagnostics = diagnostic_manager.Diagnostics(); + size_t num_diags = diagnostics.size(); + if (num_diags == 0) + return false; + + for (const auto &diag : diagnostic_manager.Diagnostics()) { + const auto *diagnostic = llvm::dyn_cast<ClangDiagnostic>(diag.get()); + if (diagnostic && diagnostic->HasFixIts()) { + for (const FixItHint &fixit : diagnostic->FixIts()) { + // This is cobbed from clang::Rewrite::FixItRewriter. + if (fixit.CodeToInsert.empty()) { + if (fixit.InsertFromRange.isValid()) { + commit.insertFromRange(fixit.RemoveRange.getBegin(), + fixit.InsertFromRange, /*afterToken=*/false, + fixit.BeforePreviousInsertions); + } else + commit.remove(fixit.RemoveRange); + } else { + if (fixit.RemoveRange.isTokenRange() || + fixit.RemoveRange.getBegin() != fixit.RemoveRange.getEnd()) + commit.replace(fixit.RemoveRange, fixit.CodeToInsert); + else + commit.insert(fixit.RemoveRange.getBegin(), fixit.CodeToInsert, + /*afterToken=*/false, fixit.BeforePreviousInsertions); + } + } + } + } + + // FIXME - do we want to try to propagate specific errors here? + if (!commit.isCommitable()) + return false; + else if (!editor.commit(commit)) + return false; + + // Now play all the edits, and stash the result in the diagnostic manager. + editor.applyRewrites(rewrites_receiver); + RewriteBuffer &main_file_buffer = + rewriter.getEditBuffer(source_manager.getMainFileID()); + + std::string fixed_expression; + llvm::raw_string_ostream out_stream(fixed_expression); + + main_file_buffer.write(out_stream); + out_stream.flush(); + diagnostic_manager.SetFixedExpression(fixed_expression); + + return true; +} + +static bool FindFunctionInModule(ConstString &mangled_name, + llvm::Module *module, const char *orig_name) { + for (const auto &func : module->getFunctionList()) { + const StringRef &name = func.getName(); + if (name.find(orig_name) != StringRef::npos) { + mangled_name.SetString(name); + return true; + } + } + + return false; +} + +lldb_private::Status ClangExpressionParser::PrepareForExecution( + lldb::addr_t &func_addr, lldb::addr_t &func_end, + lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx, + bool &can_interpret, ExecutionPolicy execution_policy) { + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + lldb_private::Status err; + + std::unique_ptr<llvm::Module> llvm_module_up( + m_code_generator->ReleaseModule()); + + if (!llvm_module_up) { + err.SetErrorToGenericError(); + err.SetErrorString("IR doesn't contain a module"); + return err; + } + + ConstString function_name; + + if (execution_policy != eExecutionPolicyTopLevel) { + // Find the actual name of the function (it's often mangled somehow) + + if (!FindFunctionInModule(function_name, llvm_module_up.get(), + m_expr.FunctionName())) { + err.SetErrorToGenericError(); + err.SetErrorStringWithFormat("Couldn't find %s() in the module", + m_expr.FunctionName()); + return err; + } else { + LLDB_LOGF(log, "Found function %s for %s", function_name.AsCString(), + m_expr.FunctionName()); + } + } + + SymbolContext sc; + + if (lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP()) { + sc = frame_sp->GetSymbolContext(lldb::eSymbolContextEverything); + } else if (lldb::TargetSP target_sp = exe_ctx.GetTargetSP()) { + sc.target_sp = target_sp; + } + + LLVMUserExpression::IRPasses custom_passes; + { + auto lang = m_expr.Language(); + LLDB_LOGF(log, "%s - Current expression language is %s\n", __FUNCTION__, + Language::GetNameForLanguageType(lang)); + lldb::ProcessSP process_sp = exe_ctx.GetProcessSP(); + if (process_sp && lang != lldb::eLanguageTypeUnknown) { + auto runtime = process_sp->GetLanguageRuntime(lang); + if (runtime) + runtime->GetIRPasses(custom_passes); + } + } + + if (custom_passes.EarlyPasses) { + LLDB_LOGF(log, + "%s - Running Early IR Passes from LanguageRuntime on " + "expression module '%s'", + __FUNCTION__, m_expr.FunctionName()); + + custom_passes.EarlyPasses->run(*llvm_module_up); + } + + execution_unit_sp = std::make_shared<IRExecutionUnit>( + m_llvm_context, // handed off here + llvm_module_up, // handed off here + function_name, exe_ctx.GetTargetSP(), sc, + m_compiler->getTargetOpts().Features); + + ClangExpressionHelper *type_system_helper = + dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper()); + ClangExpressionDeclMap *decl_map = + type_system_helper->DeclMap(); // result can be NULL + + if (decl_map) { + Target *target = exe_ctx.GetTargetPtr(); + auto &error_stream = target->GetDebugger().GetErrorStream(); + IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(), + *execution_unit_sp, error_stream, + function_name.AsCString()); + + bool ir_can_run = + ir_for_target.runOnModule(*execution_unit_sp->GetModule()); + + if (!ir_can_run) { + err.SetErrorString( + "The expression could not be prepared to run in the target"); + return err; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (execution_policy != eExecutionPolicyAlways && + execution_policy != eExecutionPolicyTopLevel) { + lldb_private::Status interpret_error; + + bool interpret_function_calls = + !process ? false : process->CanInterpretFunctionCalls(); + can_interpret = IRInterpreter::CanInterpret( + *execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), + interpret_error, interpret_function_calls); + + if (!can_interpret && execution_policy == eExecutionPolicyNever) { + err.SetErrorStringWithFormat( + "Can't evaluate the expression without a running target due to: %s", + interpret_error.AsCString()); + return err; + } + } + + if (!process && execution_policy == eExecutionPolicyAlways) { + err.SetErrorString("Expression needed to run in the target, but the " + "target can't be run"); + return err; + } + + if (!process && execution_policy == eExecutionPolicyTopLevel) { + err.SetErrorString("Top-level code needs to be inserted into a runnable " + "target, but the target can't be run"); + return err; + } + + if (execution_policy == eExecutionPolicyAlways || + (execution_policy != eExecutionPolicyTopLevel && !can_interpret)) { + if (m_expr.NeedsValidation() && process) { + if (!process->GetDynamicCheckers()) { + ClangDynamicCheckerFunctions *dynamic_checkers = + new ClangDynamicCheckerFunctions(); + + DiagnosticManager install_diagnostics; + + if (!dynamic_checkers->Install(install_diagnostics, exe_ctx)) { + if (install_diagnostics.Diagnostics().size()) + err.SetErrorString(install_diagnostics.GetString().c_str()); + else + err.SetErrorString("couldn't install checkers, unknown error"); + + return err; + } + + process->SetDynamicCheckers(dynamic_checkers); + + LLDB_LOGF(log, "== [ClangExpressionParser::PrepareForExecution] " + "Finished installing dynamic checkers =="); + } + + if (auto *checker_funcs = llvm::dyn_cast<ClangDynamicCheckerFunctions>( + process->GetDynamicCheckers())) { + IRDynamicChecks ir_dynamic_checks(*checker_funcs, + function_name.AsCString()); + + llvm::Module *module = execution_unit_sp->GetModule(); + if (!module || !ir_dynamic_checks.runOnModule(*module)) { + err.SetErrorToGenericError(); + err.SetErrorString("Couldn't add dynamic checks to the expression"); + return err; + } + + if (custom_passes.LatePasses) { + LLDB_LOGF(log, + "%s - Running Late IR Passes from LanguageRuntime on " + "expression module '%s'", + __FUNCTION__, m_expr.FunctionName()); + + custom_passes.LatePasses->run(*module); + } + } + } + } + + if (execution_policy == eExecutionPolicyAlways || + execution_policy == eExecutionPolicyTopLevel || !can_interpret) { + execution_unit_sp->GetRunnableInfo(err, func_addr, func_end); + } + } else { + execution_unit_sp->GetRunnableInfo(err, func_addr, func_end); + } + + return err; +} + +lldb_private::Status ClangExpressionParser::RunStaticInitializers( + lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx) { + lldb_private::Status err; + + lldbassert(execution_unit_sp.get()); + lldbassert(exe_ctx.HasThreadScope()); + + if (!execution_unit_sp.get()) { + err.SetErrorString( + "can't run static initializers for a NULL execution unit"); + return err; + } + + if (!exe_ctx.HasThreadScope()) { + err.SetErrorString("can't run static initializers without a thread"); + return err; + } + + std::vector<lldb::addr_t> static_initializers; + + execution_unit_sp->GetStaticInitializers(static_initializers); + + for (lldb::addr_t static_initializer : static_initializers) { + EvaluateExpressionOptions options; + + lldb::ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction( + exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(), + llvm::ArrayRef<lldb::addr_t>(), options)); + + DiagnosticManager execution_errors; + lldb::ExpressionResults results = + exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan( + exe_ctx, call_static_initializer, options, execution_errors); + + if (results != lldb::eExpressionCompleted) { + err.SetErrorStringWithFormat("couldn't run static initializer: %s", + execution_errors.GetString().c_str()); + return err; + } + } + + return err; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h new file mode 100644 index 00000000000..4a410cecb94 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h @@ -0,0 +1,182 @@ +//===-- ClangExpressionParser.h ---------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangExpressionParser_h_ +#define liblldb_ClangExpressionParser_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionParser.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-public.h" + +#include <string> +#include <vector> + +namespace clang { +class CodeCompleteConsumer; +} + +namespace lldb_private { + +class IRExecutionUnit; + +/// \class ClangExpressionParser ClangExpressionParser.h +/// "lldb/Expression/ClangExpressionParser.h" Encapsulates an instance of +/// Clang that can parse expressions. +/// +/// ClangExpressionParser is responsible for preparing an instance of +/// ClangExpression for execution. ClangExpressionParser uses ClangExpression +/// as a glorified parameter list, performing the required parsing and +/// conversion to formats (DWARF bytecode, or JIT compiled machine code) that +/// can be executed. +class ClangExpressionParser : public ExpressionParser { +public: + /// Constructor + /// + /// Initializes class variables. + /// + /// \param[in] exe_scope + /// If non-NULL, an execution context scope that can help to + /// correctly create an expression with a valid process for + /// optional tuning Objective-C runtime support. Can be NULL. + /// + /// \param[in] expr + /// The expression to be parsed. + /// + /// @param[in] include_directories + /// List of include directories that should be used when parsing the + /// expression. + /// + /// @param[in] filename + /// Name of the source file that should be used when rendering + /// diagnostics (i.e. errors, warnings or notes from Clang). + ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr, + bool generate_debug_info, + std::vector<std::string> include_directories = {}, + std::string filename = "<clang expression>"); + + /// Destructor + ~ClangExpressionParser() override; + + bool Complete(CompletionRequest &request, unsigned line, unsigned pos, + unsigned typed_pos) override; + + /// Parse a single expression and convert it to IR using Clang. Don't wrap + /// the expression in anything at all. + /// + /// \param[in] diagnostic_manager + /// The diagnostic manager to report errors to. + /// + /// \return + /// The number of errors encountered during parsing. 0 means + /// success. + unsigned Parse(DiagnosticManager &diagnostic_manager); + + bool RewriteExpression(DiagnosticManager &diagnostic_manager) override; + + /// Ready an already-parsed expression for execution, possibly evaluating it + /// statically. + /// + /// \param[out] func_addr + /// The address to which the function has been written. + /// + /// \param[out] func_end + /// The end of the function's allocated memory region. (func_addr + /// and func_end do not delimit an allocated region; the allocated + /// region may begin before func_addr.) + /// + /// \param[in] execution_unit_sp + /// After parsing, ownership of the execution unit for + /// for the expression is handed to this shared pointer. + /// + /// \param[in] exe_ctx + /// The execution context to write the function into. + /// + /// \param[in] execution_policy + /// Determines whether the expression must be JIT-compiled, must be + /// evaluated statically, or whether this decision may be made + /// opportunistically. + /// + /// \return + /// An error code indicating the success or failure of the operation. + /// Test with Success(). + Status + PrepareForExecution(lldb::addr_t &func_addr, lldb::addr_t &func_end, + lldb::IRExecutionUnitSP &execution_unit_sp, + ExecutionContext &exe_ctx, bool &can_interpret, + lldb_private::ExecutionPolicy execution_policy) override; + + /// Run all static initializers for an execution unit. + /// + /// \param[in] execution_unit_sp + /// The execution unit. + /// + /// \param[in] exe_ctx + /// The execution context to use when running them. Thread can't be null. + /// + /// \return + /// The error code indicating the + Status RunStaticInitializers(lldb::IRExecutionUnitSP &execution_unit_sp, + ExecutionContext &exe_ctx); + + /// Returns a string representing current ABI. + /// + /// \param[in] target_arch + /// The target architecture. + /// + /// \return + /// A string representing target ABI for the current architecture. + std::string GetClangTargetABI(const ArchSpec &target_arch); + +private: + /// Parses the expression. + /// + /// \param[in] diagnostic_manager + /// The diagnostic manager that should receive the diagnostics + /// from the parsing process. + /// + /// \param[in] completion + /// The completion consumer that should be used during parsing + /// (or a nullptr if no consumer should be attached). + /// + /// \param[in] completion_line + /// The line in which the completion marker should be placed. + /// The first line is represented by the value 0. + /// + /// \param[in] completion_column + /// The column in which the completion marker should be placed. + /// The first column is represented by the value 0. + /// + /// \return + /// The number of parsing errors. + unsigned ParseInternal(DiagnosticManager &diagnostic_manager, + clang::CodeCompleteConsumer *completion = nullptr, + unsigned completion_line = 0, + unsigned completion_column = 0); + + std::unique_ptr<llvm::LLVMContext> + m_llvm_context; ///< The LLVM context to generate IR into + std::unique_ptr<clang::CompilerInstance> + m_compiler; ///< The Clang compiler used to parse expressions into IR + std::unique_ptr<clang::CodeGenerator> + m_code_generator; ///< The Clang object that generates IR + + class LLDBPreprocessorCallbacks; + LLDBPreprocessorCallbacks *m_pp_callbacks; ///< Called when the preprocessor + ///encounters module imports + std::unique_ptr<ClangASTContext> m_ast_context; + + std::vector<std::string> m_include_directories; + /// File name used for the user expression. + std::string m_filename; +}; +} + +#endif // liblldb_ClangExpressionParser_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp new file mode 100644 index 00000000000..7ebb5fee1ec --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -0,0 +1,507 @@ +//===-- ClangExpressionSourceCode.cpp ---------------------------*- C++ -*-===// +// +// 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 "ClangExpressionSourceCode.h" + +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringRef.h" + +#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" +#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/DebugMacros.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; + +#define PREFIX_NAME "<lldb wrapper prefix>" + +const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME; + +const char *ClangExpressionSourceCode::g_expression_prefix = +"#line 1 \"" PREFIX_NAME R"(" +#ifndef offsetof +#define offsetof(t, d) __builtin_offsetof(t, d) +#endif +#ifndef NULL +#define NULL (__null) +#endif +#ifndef Nil +#define Nil (__null) +#endif +#ifndef nil +#define nil (__null) +#endif +#ifndef YES +#define YES ((BOOL)1) +#endif +#ifndef NO +#define NO ((BOOL)0) +#endif +typedef __INT8_TYPE__ int8_t; +typedef __UINT8_TYPE__ uint8_t; +typedef __INT16_TYPE__ int16_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __INT32_TYPE__ int32_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __INT64_TYPE__ int64_t; +typedef __UINT64_TYPE__ uint64_t; +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef unsigned short unichar; +extern "C" +{ + int printf(const char * __restrict, ...); +} +)"; + +namespace { + +class AddMacroState { + enum State { + CURRENT_FILE_NOT_YET_PUSHED, + CURRENT_FILE_PUSHED, + CURRENT_FILE_POPPED + }; + +public: + AddMacroState(const FileSpec ¤t_file, const uint32_t current_file_line) + : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file), + m_current_file_line(current_file_line) {} + + void StartFile(const FileSpec &file) { + m_file_stack.push_back(file); + if (file == m_current_file) + m_state = CURRENT_FILE_PUSHED; + } + + void EndFile() { + if (m_file_stack.size() == 0) + return; + + FileSpec old_top = m_file_stack.back(); + m_file_stack.pop_back(); + if (old_top == m_current_file) + m_state = CURRENT_FILE_POPPED; + } + + // An entry is valid if it occurs before the current line in the current + // file. + bool IsValidEntry(uint32_t line) { + switch (m_state) { + case CURRENT_FILE_NOT_YET_PUSHED: + return true; + case CURRENT_FILE_PUSHED: + // If we are in file included in the current file, the entry should be + // added. + if (m_file_stack.back() != m_current_file) + return true; + + return line < m_current_file_line; + default: + return false; + } + } + +private: + std::vector<FileSpec> m_file_stack; + State m_state; + FileSpec m_current_file; + uint32_t m_current_file_line; +}; + +} // anonymous namespace + +static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit, + AddMacroState &state, StreamString &stream) { + if (dm == nullptr) + return; + + for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) { + const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i); + uint32_t line; + + switch (entry.GetType()) { + case DebugMacroEntry::DEFINE: + if (state.IsValidEntry(entry.GetLineNumber())) + stream.Printf("#define %s\n", entry.GetMacroString().AsCString()); + else + return; + break; + case DebugMacroEntry::UNDEF: + if (state.IsValidEntry(entry.GetLineNumber())) + stream.Printf("#undef %s\n", entry.GetMacroString().AsCString()); + else + return; + break; + case DebugMacroEntry::START_FILE: + line = entry.GetLineNumber(); + if (state.IsValidEntry(line)) + state.StartFile(entry.GetFileSpec(comp_unit)); + else + return; + break; + case DebugMacroEntry::END_FILE: + state.EndFile(); + break; + case DebugMacroEntry::INDIRECT: + AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream); + break; + default: + // This is an unknown/invalid entry. Ignore. + break; + } + } +} + +lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode( + llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix, + llvm::StringRef body, Wrapping wrap) + : ExpressionSourceCode(name, prefix, body, wrap) { + // Use #line markers to pretend that we have a single-line source file + // containing only the user expression. This will hide our wrapper code + // from the user when we render diagnostics with Clang. + m_start_marker = "#line 1 \"" + filename.str() + "\"\n"; + m_end_marker = "\n;\n#line 1 \"<lldb wrapper suffix>\"\n"; +} + +namespace { +/// Allows checking if a token is contained in a given expression. +class TokenVerifier { + /// The tokens we found in the expression. + llvm::StringSet<> m_tokens; + +public: + TokenVerifier(std::string body); + /// Returns true iff the given expression body contained a token with the + /// given content. + bool hasToken(llvm::StringRef token) const { + return m_tokens.find(token) != m_tokens.end(); + } +}; +} // namespace + +TokenVerifier::TokenVerifier(std::string body) { + using namespace clang; + + // We only care about tokens and not their original source locations. If we + // move the whole expression to only be in one line we can simplify the + // following code that extracts the token contents. + std::replace(body.begin(), body.end(), '\n', ' '); + std::replace(body.begin(), body.end(), '\r', ' '); + + FileSystemOptions file_opts; + FileManager file_mgr(file_opts, + FileSystem::Instance().GetVirtualFileSystem()); + + // Let's build the actual source code Clang needs and setup some utility + // objects. + llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts( + new DiagnosticOptions()); + DiagnosticsEngine diags(diag_ids, diags_opts); + clang::SourceManager SM(diags, file_mgr); + auto buf = llvm::MemoryBuffer::getMemBuffer(body); + + FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get()); + + // Let's just enable the latest ObjC and C++ which should get most tokens + // right. + LangOptions Opts; + Opts.ObjC = true; + Opts.DollarIdents = true; + Opts.CPlusPlus17 = true; + Opts.LineComment = true; + + Lexer lex(FID, buf.get(), SM, Opts); + + Token token; + bool exit = false; + while (!exit) { + // Returns true if this is the last token we get from the lexer. + exit = lex.LexFromRawLexer(token); + + // Extract the column number which we need to extract the token content. + // Our expression is just one line, so we don't need to handle any line + // numbers here. + bool invalid = false; + unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid); + if (invalid) + continue; + // Column numbers start at 1, but indexes in our string start at 0. + --start; + + // Annotations don't have a length, so let's skip them. + if (token.isAnnotation()) + continue; + + // Extract the token string from our source code and store it. + std::string token_str = body.substr(start, token.getLength()); + if (token_str.empty()) + continue; + m_tokens.insert(token_str); + } +} + +static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp, + StreamString &stream, + const std::string &expr, + lldb::LanguageType wrapping_language) { + TokenVerifier tokens(expr); + + for (size_t i = 0; i < var_list_sp->GetSize(); i++) { + lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i); + + ConstString var_name = var_sp->GetName(); + + + // We can check for .block_descriptor w/o checking for langauge since this + // is not a valid identifier in either C or C++. + if (!var_name || var_name == ".block_descriptor") + continue; + + if (!expr.empty() && !tokens.hasToken(var_name.GetStringRef())) + continue; + + if ((var_name == "self" || var_name == "_cmd") && + (wrapping_language == lldb::eLanguageTypeObjC || + wrapping_language == lldb::eLanguageTypeObjC_plus_plus)) + continue; + + if (var_name == "this" && + wrapping_language == lldb::eLanguageTypeC_plus_plus) + continue; + + stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString()); + } +} + +bool ClangExpressionSourceCode::GetText( + std::string &text, lldb::LanguageType wrapping_language, bool static_method, + ExecutionContext &exe_ctx, bool add_locals, bool force_add_all_locals, + llvm::ArrayRef<std::string> modules) const { + const char *target_specific_defines = "typedef signed char BOOL;\n"; + std::string module_macros; + + Target *target = exe_ctx.GetTargetPtr(); + if (target) { + if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64 || + target->GetArchitecture().GetMachine() == llvm::Triple::aarch64_32) { + target_specific_defines = "typedef bool BOOL;\n"; + } + if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) { + if (lldb::PlatformSP platform_sp = target->GetPlatform()) { + static ConstString g_platform_ios_simulator("ios-simulator"); + if (platform_sp->GetPluginName() == g_platform_ios_simulator) { + target_specific_defines = "typedef bool BOOL;\n"; + } + } + } + + ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor(); + auto *persistent_vars = llvm::cast<ClangPersistentVariables>( + target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); + if (decl_vendor && persistent_vars) { + const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = + persistent_vars->GetHandLoadedClangModules(); + ClangModulesDeclVendor::ModuleVector modules_for_macros; + + for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) { + modules_for_macros.push_back(module); + } + + if (target->GetEnableAutoImportClangModules()) { + if (StackFrame *frame = exe_ctx.GetFramePtr()) { + if (Block *block = frame->GetFrameBlock()) { + SymbolContext sc; + + block->CalculateSymbolContext(&sc); + + if (sc.comp_unit) { + StreamString error_stream; + + decl_vendor->AddModulesForCompileUnit( + *sc.comp_unit, modules_for_macros, error_stream); + } + } + } + } + + decl_vendor->ForEachMacro( + modules_for_macros, + [&module_macros](const std::string &expansion) -> bool { + module_macros.append(expansion); + module_macros.append("\n"); + return false; + }); + } + } + + StreamString debug_macros_stream; + StreamString lldb_local_var_decls; + if (StackFrame *frame = exe_ctx.GetFramePtr()) { + const SymbolContext &sc = frame->GetSymbolContext( + lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry); + + if (sc.comp_unit && sc.line_entry.IsValid()) { + DebugMacros *dm = sc.comp_unit->GetDebugMacros(); + if (dm) { + AddMacroState state(sc.line_entry.file, sc.line_entry.line); + AddMacros(dm, sc.comp_unit, state, debug_macros_stream); + } + } + + if (add_locals) + if (target->GetInjectLocalVariables(&exe_ctx)) { + lldb::VariableListSP var_list_sp = + frame->GetInScopeVariableList(false, true); + AddLocalVariableDecls(var_list_sp, lldb_local_var_decls, + force_add_all_locals ? "" : m_body, + wrapping_language); + } + } + + if (m_wrap) { + switch (wrapping_language) { + default: + return false; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + break; + } + + // Generate a list of @import statements that will import the specified + // module into our expression. + std::string module_imports; + for (const std::string &module : modules) { + module_imports.append("@import "); + module_imports.append(module); + module_imports.append(";\n"); + } + + StreamString wrap_stream; + + wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(), + debug_macros_stream.GetData(), g_expression_prefix, + target_specific_defines, m_prefix.c_str()); + + // First construct a tagged form of the user expression so we can find it + // later: + std::string tagged_body; + switch (wrapping_language) { + default: + tagged_body = m_body; + break; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + tagged_body.append(m_start_marker); + tagged_body.append(m_body); + tagged_body.append(m_end_marker); + break; + } + switch (wrapping_language) { + default: + break; + case lldb::eLanguageTypeC: + wrap_stream.Printf("%s" + "void \n" + "%s(void *$__lldb_arg) \n" + "{ \n" + " %s; \n" + "%s" + "} \n", + module_imports.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); + break; + case lldb::eLanguageTypeC_plus_plus: + wrap_stream.Printf("%s" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) \n" + "{ \n" + " %s; \n" + "%s" + "} \n", + module_imports.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); + break; + case lldb::eLanguageTypeObjC: + if (static_method) { + wrap_stream.Printf( + "%s" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "%s" + "} \n" + "@end \n", + module_imports.c_str(), m_name.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); + } else { + wrap_stream.Printf( + "%s" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "%s" + "} \n" + "@end \n", + module_imports.c_str(), m_name.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); + } + break; + } + + text = wrap_stream.GetString(); + } else { + text.append(m_body); + } + + return true; +} + +bool ClangExpressionSourceCode::GetOriginalBodyBounds( + std::string transformed_text, lldb::LanguageType wrapping_language, + size_t &start_loc, size_t &end_loc) { + switch (wrapping_language) { + default: + return false; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + break; + } + + start_loc = transformed_text.find(m_start_marker); + if (start_loc == std::string::npos) + return false; + start_loc += m_start_marker.size(); + end_loc = transformed_text.find(m_end_marker); + return end_loc != std::string::npos; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h new file mode 100644 index 00000000000..1d159670b96 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -0,0 +1,80 @@ +//===-- ClangExpressionSourceCode.h -----------------------------*- C++ -*-===// +// +// 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 liblldb_ClangExpressionSourceCode_h +#define liblldb_ClangExpressionSourceCode_h + +#include "lldb/Expression/Expression.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +#include <string> + +namespace lldb_private { + +class ExecutionContext; + +class ClangExpressionSourceCode : public ExpressionSourceCode { +public: + /// The file name we use for the wrapper code that we inject before + /// the user expression. + static const llvm::StringRef g_prefix_file_name; + static const char *g_expression_prefix; + + static ClangExpressionSourceCode *CreateWrapped(llvm::StringRef filename, + llvm::StringRef prefix, + llvm::StringRef body) { + return new ClangExpressionSourceCode(filename, "$__lldb_expr", prefix, body, + Wrap); + } + + /// Generates the source code that will evaluate the expression. + /// + /// \param text output parameter containing the source code string. + /// \param wrapping_language If the expression is supossed to be wrapped, + /// then this is the language that should be used for that. + /// \param static_method True iff the expression is valuated inside a static + /// Objective-C method. + /// \param exe_ctx The execution context in which the expression will be + /// evaluated. + /// \param add_locals True iff local variables should be injected into the + /// expression source code. + /// \param force_add_all_locals True iff all local variables should be + /// injected even if they are not used in the expression. + /// \param modules A list of (C++) modules that the expression should import. + /// + /// \return true iff the source code was successfully generated. + bool GetText(std::string &text, lldb::LanguageType wrapping_language, + bool static_method, ExecutionContext &exe_ctx, bool add_locals, + bool force_add_all_locals, + llvm::ArrayRef<std::string> modules) const; + + // Given a string returned by GetText, find the beginning and end of the body + // passed to CreateWrapped. Return true if the bounds could be found. This + // will also work on text with FixItHints applied. + bool GetOriginalBodyBounds(std::string transformed_text, + lldb::LanguageType wrapping_language, + size_t &start_loc, size_t &end_loc); + +protected: + ClangExpressionSourceCode(llvm::StringRef filename, llvm::StringRef name, + llvm::StringRef prefix, llvm::StringRef body, + Wrapping wrap); + +private: + /// String marking the start of the user expression. + std::string m_start_marker; + /// String marking the end of the user expression. + std::string m_end_marker; +}; + +} // namespace lldb_private + +#endif diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp new file mode 100644 index 00000000000..b5a2c80b534 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp @@ -0,0 +1,66 @@ +//===-- ClangExpressionVariable.cpp -----------------------------*- C++ -*-===// +// +// 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 "ClangExpressionVariable.h" + +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Stream.h" +#include "clang/AST/ASTContext.h" + +using namespace lldb_private; +using namespace clang; + +ClangExpressionVariable::ClangExpressionVariable( + ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, + uint32_t addr_byte_size) + : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(), + m_jit_vars() { + m_flags = EVNone; + m_frozen_sp = + ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size); +} + +ClangExpressionVariable::ClangExpressionVariable( + ExecutionContextScope *exe_scope, Value &value, ConstString name, + uint16_t flags) + : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(), + m_jit_vars() { + m_flags = flags; + m_frozen_sp = ValueObjectConstResult::Create(exe_scope, value, name); +} + +ClangExpressionVariable::ClangExpressionVariable( + const lldb::ValueObjectSP &valobj_sp) + : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(), + m_jit_vars() { + m_flags = EVNone; + m_frozen_sp = valobj_sp; +} + +ClangExpressionVariable::ClangExpressionVariable( + ExecutionContextScope *exe_scope, ConstString name, + const TypeFromUser &user_type, lldb::ByteOrder byte_order, + uint32_t addr_byte_size) + : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(), + m_jit_vars() { + m_flags = EVNone; + m_frozen_sp = + ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size); + SetName(name); + SetCompilerType(user_type); +} + +TypeFromUser ClangExpressionVariable::GetTypeFromUser() { + TypeFromUser tfu(m_frozen_sp->GetCompilerType()); + return tfu; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h new file mode 100644 index 00000000000..0e6de28ee4d --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h @@ -0,0 +1,204 @@ +//===-- ClangExpressionVariable.h -------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangExpressionVariable_h_ +#define liblldb_ClangExpressionVariable_h_ + +#include <signal.h> +#include <stdint.h> +#include <string.h> + +#include <map> +#include <string> +#include <vector> + +#include "llvm/Support/Casting.h" + +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-public.h" + +namespace llvm { +class Value; +} + +namespace lldb_private { + +class ValueObjectConstResult; + +/// \class ClangExpressionVariable ClangExpressionVariable.h +/// "lldb/Expression/ClangExpressionVariable.h" Encapsulates one variable for +/// the expression parser. +/// +/// The expression parser uses variables in three different contexts: +/// +/// First, it stores persistent variables along with the process for use in +/// expressions. These persistent variables contain their own data and are +/// typed. +/// +/// Second, in an interpreted expression, it stores the local variables for +/// the expression along with the expression. These variables contain their +/// own data and are typed. +/// +/// Third, in a JIT-compiled expression, it stores the variables that the +/// expression needs to have materialized and dematerialized at each +/// execution. These do not contain their own data but are named and typed. +/// +/// This class supports all of these use cases using simple type polymorphism, +/// and provides necessary support methods. Its interface is RTTI-neutral. +class ClangExpressionVariable : public ExpressionVariable { +public: + ClangExpressionVariable(ExecutionContextScope *exe_scope, + lldb::ByteOrder byte_order, uint32_t addr_byte_size); + + ClangExpressionVariable(ExecutionContextScope *exe_scope, Value &value, + ConstString name, uint16_t flags = EVNone); + + ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp); + + ClangExpressionVariable(ExecutionContextScope *exe_scope, + ConstString name, + const TypeFromUser &user_type, + lldb::ByteOrder byte_order, uint32_t addr_byte_size); + + /// Utility functions for dealing with ExpressionVariableLists in Clang- + /// specific ways + + /// Finds a variable by NamedDecl in the list. + /// + /// \return + /// The variable requested, or NULL if that variable is not in the list. + static ClangExpressionVariable * + FindVariableInList(ExpressionVariableList &list, const clang::NamedDecl *decl, + uint64_t parser_id) { + lldb::ExpressionVariableSP var_sp; + for (size_t index = 0, size = list.GetSize(); index < size; ++index) { + var_sp = list.GetVariableAtIndex(index); + + if (ClangExpressionVariable *clang_var = + llvm::dyn_cast<ClangExpressionVariable>(var_sp.get())) { + ClangExpressionVariable::ParserVars *parser_vars = + clang_var->GetParserVars(parser_id); + + if (parser_vars && parser_vars->m_named_decl == decl) + return clang_var; + } + } + return nullptr; + } + + /// If the variable contains its own data, make a Value point at it. If \a + /// exe_ctx in not NULL, the value will be resolved in with that execution + /// context. + /// + /// \param[in] value + /// The value to point at the data. + /// + /// \param[in] exe_ctx + /// The execution context to use to resolve \a value. + /// + /// \return + /// True on success; false otherwise (in particular, if this variable + /// does not contain its own data). + bool PointValueAtData(Value &value, ExecutionContext *exe_ctx); + + /// The following values should not live beyond parsing + class ParserVars { + public: + ParserVars() + : m_parser_type(), m_named_decl(nullptr), m_llvm_value(nullptr), + m_lldb_value(), m_lldb_var(), m_lldb_sym(nullptr) {} + + TypeFromParser + m_parser_type; ///< The type of the variable according to the parser + const clang::NamedDecl + *m_named_decl; ///< The Decl corresponding to this variable + llvm::Value *m_llvm_value; ///< The IR value corresponding to this variable; + ///usually a GlobalValue + lldb_private::Value + m_lldb_value; ///< The value found in LLDB for this variable + lldb::VariableSP m_lldb_var; ///< The original variable for this variable + const lldb_private::Symbol *m_lldb_sym; ///< The original symbol for this + ///variable, if it was a symbol + }; + +private: + typedef std::map<uint64_t, ParserVars> ParserVarMap; + ParserVarMap m_parser_vars; + +public: + /// Make this variable usable by the parser by allocating space for parser- + /// specific variables + void EnableParserVars(uint64_t parser_id) { + m_parser_vars.insert(std::make_pair(parser_id, ParserVars())); + } + + /// Deallocate parser-specific variables + void DisableParserVars(uint64_t parser_id) { m_parser_vars.erase(parser_id); } + + /// Access parser-specific variables + ParserVars *GetParserVars(uint64_t parser_id) { + ParserVarMap::iterator i = m_parser_vars.find(parser_id); + + if (i == m_parser_vars.end()) + return nullptr; + else + return &i->second; + } + + /// The following values are valid if the variable is used by JIT code + struct JITVars { + JITVars() : m_alignment(0), m_size(0), m_offset(0) {} + + lldb::offset_t + m_alignment; ///< The required alignment of the variable, in bytes + size_t m_size; ///< The space required for the variable, in bytes + lldb::offset_t + m_offset; ///< The offset of the variable in the struct, in bytes + }; + +private: + typedef std::map<uint64_t, JITVars> JITVarMap; + JITVarMap m_jit_vars; + +public: + /// Make this variable usable for materializing for the JIT by allocating + /// space for JIT-specific variables + void EnableJITVars(uint64_t parser_id) { + m_jit_vars.insert(std::make_pair(parser_id, JITVars())); + } + + /// Deallocate JIT-specific variables + void DisableJITVars(uint64_t parser_id) { m_jit_vars.erase(parser_id); } + + JITVars *GetJITVars(uint64_t parser_id) { + JITVarMap::iterator i = m_jit_vars.find(parser_id); + + if (i == m_jit_vars.end()) + return nullptr; + else + return &i->second; + } + + TypeFromUser GetTypeFromUser(); + + // llvm casting support + static bool classof(const ExpressionVariable *ev) { + return ev->getKind() == ExpressionVariable::eKindClang; + } + + /// Members + DISALLOW_COPY_AND_ASSIGN(ClangExpressionVariable); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionVariable_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp new file mode 100644 index 00000000000..7f7c0a97f53 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp @@ -0,0 +1,216 @@ +//===-- ClangFunctionCaller.cpp ---------------------------------*- C++ -*-===// +// +// 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 "ClangFunctionCaller.h" + +#include "ASTStructExtractor.h" +#include "ClangExpressionParser.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/Module.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +using namespace lldb_private; + +char ClangFunctionCaller::ID; + +// ClangFunctionCaller constructor +ClangFunctionCaller::ClangFunctionCaller(ExecutionContextScope &exe_scope, + const CompilerType &return_type, + const Address &functionAddress, + const ValueList &arg_value_list, + const char *name) + : FunctionCaller(exe_scope, return_type, functionAddress, arg_value_list, + name), + m_type_system_helper(*this) { + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunctionCaller without a process. + assert(m_jit_process_wp.lock()); +} + +// Destructor +ClangFunctionCaller::~ClangFunctionCaller() {} + +unsigned + +ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp, + DiagnosticManager &diagnostic_manager) { + if (m_compiled) + return 0; + + // Compilation might call code, make sure to keep on the thread the caller + // indicated. + ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher( + thread_to_use_sp); + + // FIXME: How does clang tell us there's no return value? We need to handle + // that case. + unsigned num_errors = 0; + + std::string return_type_str( + m_function_return_type.GetTypeName().AsCString("")); + + // Cons up the function we're going to wrap our call in, then compile it... + // We declare the function "extern "C"" because the compiler might be in C++ + // mode which would mangle the name and then we couldn't find it again... + m_wrapper_function_text.clear(); + m_wrapper_function_text.append("extern \"C\" void "); + m_wrapper_function_text.append(m_wrapper_function_name); + m_wrapper_function_text.append(" (void *input)\n{\n struct "); + m_wrapper_function_text.append(m_wrapper_struct_name); + m_wrapper_function_text.append(" \n {\n"); + m_wrapper_function_text.append(" "); + m_wrapper_function_text.append(return_type_str); + m_wrapper_function_text.append(" (*fn_ptr) ("); + + // Get the number of arguments. If we have a function type and it is + // prototyped, trust that, otherwise use the values we were given. + + // FIXME: This will need to be extended to handle Variadic functions. We'll + // need + // to pull the defined arguments out of the function, then add the types from + // the arguments list for the variable arguments. + + uint32_t num_args = UINT32_MAX; + bool trust_function = false; + // GetArgumentCount returns -1 for an unprototyped function. + CompilerType function_clang_type; + if (m_function_ptr) { + function_clang_type = m_function_ptr->GetCompilerType(); + if (function_clang_type) { + int num_func_args = function_clang_type.GetFunctionArgumentCount(); + if (num_func_args >= 0) { + trust_function = true; + num_args = num_func_args; + } + } + } + + if (num_args == UINT32_MAX) + num_args = m_arg_values.GetSize(); + + std::string args_buffer; // This one stores the definition of all the args in + // "struct caller". + std::string args_list_buffer; // This one stores the argument list called from + // the structure. + for (size_t i = 0; i < num_args; i++) { + std::string type_name; + + if (trust_function) { + type_name = function_clang_type.GetFunctionArgumentTypeAtIndex(i) + .GetTypeName() + .AsCString(""); + } else { + CompilerType clang_qual_type = + m_arg_values.GetValueAtIndex(i)->GetCompilerType(); + if (clang_qual_type) { + type_name = clang_qual_type.GetTypeName().AsCString(""); + } else { + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "Could not determine type of input value %" PRIu64 ".", + (uint64_t)i); + return 1; + } + } + + m_wrapper_function_text.append(type_name); + if (i < num_args - 1) + m_wrapper_function_text.append(", "); + + char arg_buf[32]; + args_buffer.append(" "); + args_buffer.append(type_name); + snprintf(arg_buf, 31, "arg_%" PRIu64, (uint64_t)i); + args_buffer.push_back(' '); + args_buffer.append(arg_buf); + args_buffer.append(";\n"); + + args_list_buffer.append("__lldb_fn_data->"); + args_list_buffer.append(arg_buf); + if (i < num_args - 1) + args_list_buffer.append(", "); + } + m_wrapper_function_text.append( + ");\n"); // Close off the function calling prototype. + + m_wrapper_function_text.append(args_buffer); + + m_wrapper_function_text.append(" "); + m_wrapper_function_text.append(return_type_str); + m_wrapper_function_text.append(" return_value;"); + m_wrapper_function_text.append("\n };\n struct "); + m_wrapper_function_text.append(m_wrapper_struct_name); + m_wrapper_function_text.append("* __lldb_fn_data = (struct "); + m_wrapper_function_text.append(m_wrapper_struct_name); + m_wrapper_function_text.append(" *) input;\n"); + + m_wrapper_function_text.append( + " __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr ("); + m_wrapper_function_text.append(args_list_buffer); + m_wrapper_function_text.append(");\n}\n"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + LLDB_LOGF(log, "Expression: \n\n%s\n\n", m_wrapper_function_text.c_str()); + + // Okay, now compile this expression + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + if (jit_process_sp) { + const bool generate_debug_info = true; + auto *clang_parser = new ClangExpressionParser(jit_process_sp.get(), *this, + generate_debug_info); + num_errors = clang_parser->Parse(diagnostic_manager); + m_parser.reset(clang_parser); + } else { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "no process - unable to inject function"); + num_errors = 1; + } + + m_compiled = (num_errors == 0); + + if (!m_compiled) + return num_errors; + + return num_errors; +} + +clang::ASTConsumer * +ClangFunctionCaller::ClangFunctionCallerHelper::ASTTransformer( + clang::ASTConsumer *passthrough) { + m_struct_extractor.reset(new ASTStructExtractor( + passthrough, m_owner.GetWrapperStructName(), m_owner)); + + return m_struct_extractor.get(); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h new file mode 100644 index 00000000000..150a913152d --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h @@ -0,0 +1,153 @@ +//===-- ClangFunctionCaller.h -----------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangFunctionCaller_h_ +#define liblldb_ClangFunctionCaller_h_ + +#include "ClangExpressionHelper.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class ASTStructExtractor; +class ClangExpressionParser; + +/// \class ClangFunctionCaller ClangFunctionCaller.h +/// "lldb/Expression/ClangFunctionCaller.h" Encapsulates a function that can +/// be called. +/// +/// A given ClangFunctionCaller object can handle a single function signature. +/// Once constructed, it can set up any number of concurrent calls to +/// functions with that signature. +/// +/// It performs the call by synthesizing a structure that contains the pointer +/// to the function and the arguments that should be passed to that function, +/// and producing a special-purpose JIT-compiled function that accepts a void* +/// pointing to this struct as its only argument and calls the function in the +/// struct with the written arguments. This method lets Clang handle the +/// vagaries of function calling conventions. +/// +/// The simplest use of the ClangFunctionCaller is to construct it with a +/// function representative of the signature you want to use, then call +/// ExecuteFunction(ExecutionContext &, Stream &, Value &). +/// +/// If you need to reuse the arguments for several calls, you can call +/// InsertFunction() followed by WriteFunctionArguments(), which will return +/// the location of the args struct for the wrapper function in args_addr_ref. +/// +/// If you need to call the function on the thread plan stack, you can also +/// call InsertFunction() followed by GetThreadPlanToCallFunction(). +/// +/// Any of the methods that take arg_addr_ptr or arg_addr_ref can be passed a +/// pointer set to LLDB_INVALID_ADDRESS and new structure will be allocated +/// and its address returned in that variable. +/// +/// Any of the methods that take arg_addr_ptr can be passed NULL, and the +/// argument space will be managed for you. +class ClangFunctionCaller : public FunctionCaller { + friend class ASTStructExtractor; + + class ClangFunctionCallerHelper : public ClangExpressionHelper { + public: + ClangFunctionCallerHelper(ClangFunctionCaller &owner) : m_owner(owner) {} + + ~ClangFunctionCallerHelper() override = default; + + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + ClangExpressionDeclMap *DeclMap() override { return nullptr; } + + /// Return the object that the parser should allow to access ASTs. May be + /// NULL if the ASTs do not need to be transformed. + /// + /// \param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + clang::ASTConsumer * + ASTTransformer(clang::ASTConsumer *passthrough) override; + + private: + ClangFunctionCaller &m_owner; + std::unique_ptr<ASTStructExtractor> m_struct_extractor; ///< The class that + ///generates the + ///argument struct + ///layout. + }; + + // LLVM RTTI support + static char ID; + +public: + bool isA(const void *ClassID) const override { + return ClassID == &ID || FunctionCaller::isA(ClassID); + } + static bool classof(const Expression *obj) { return obj->isA(&ID); } + + /// Constructor + /// + /// \param[in] exe_scope + /// An execution context scope that gets us at least a target and + /// process. + /// + /// \param[in] return_type + /// A compiler type for the function result. Should be + /// defined in ast_context. + /// + /// \param[in] function_address + /// The address of the function to call. + /// + /// \param[in] arg_value_list + /// The default values to use when calling this function. Can + /// be overridden using WriteFunctionArguments(). + ClangFunctionCaller(ExecutionContextScope &exe_scope, + const CompilerType &return_type, + const Address &function_address, + const ValueList &arg_value_list, const char *name); + + ~ClangFunctionCaller() override; + + /// Compile the wrapper function + /// + /// \param[in] thread_to_use_sp + /// Compilation might end up calling functions. Pass in the thread you + /// want the compilation to use. If you pass in an empty ThreadSP it will + /// use the currently selected thread. + /// + /// \param[in] diagnostic_manager + /// The diagnostic manager to report parser errors to. + /// + /// \return + /// The number of errors. + unsigned CompileFunction(lldb::ThreadSP thread_to_use_sp, + DiagnosticManager &diagnostic_manager) override; + + ExpressionTypeSystemHelper *GetTypeSystemHelper() override { + return &m_type_system_helper; + } + +protected: + const char *GetWrapperStructName() { return m_wrapper_struct_name.c_str(); } + +private: + // For ClangFunctionCaller only + + // Note: the parser needs to be destructed before the execution unit, so + // declare the execution unit first. + ClangFunctionCallerHelper m_type_system_helper; +}; + +} // namespace lldb_private + +#endif // liblldb_ClangFunctionCaller_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp new file mode 100644 index 00000000000..42d3f22014d --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp @@ -0,0 +1,167 @@ +//===-- ClangHost.cpp -------------------------------------------*- C++ -*-===// +// +// 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 "ClangHost.h" + +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" + +#include <string> + +using namespace lldb_private; + +static bool VerifyClangPath(const llvm::Twine &clang_path) { + if (FileSystem::Instance().IsDirectory(clang_path)) + return true; + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + LLDB_LOGF(log, + "VerifyClangPath(): " + "failed to stat clang resource directory at \"%s\"", + clang_path.str().c_str()); + return false; +} + +/// +/// This will compute the clang resource directory assuming that clang was +/// installed with the same prefix as lldb. +/// +/// If verify is true, the first candidate resource directory will be returned. +/// This mode is only used for testing. +/// +static bool DefaultComputeClangResourceDirectory(FileSpec &lldb_shlib_spec, + FileSpec &file_spec, + bool verify) { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + std::string raw_path = lldb_shlib_spec.GetPath(); + llvm::StringRef parent_dir = llvm::sys::path::parent_path(raw_path); + + static const llvm::StringRef kResourceDirSuffixes[] = { + // LLVM.org's build of LLDB uses the clang resource directory placed + // in $install_dir/lib{,64}/clang/$clang_version. + "lib" CLANG_LIBDIR_SUFFIX "/clang/" CLANG_VERSION_STRING, + // swift-lldb uses the clang resource directory copied from swift, which + // by default is placed in $install_dir/lib{,64}/lldb/clang. LLDB places + // it there, so we use LLDB_LIBDIR_SUFFIX. + "lib" LLDB_LIBDIR_SUFFIX "/lldb/clang", + }; + + for (const auto &Suffix : kResourceDirSuffixes) { + llvm::SmallString<256> clang_dir(parent_dir); + llvm::SmallString<32> relative_path(Suffix); + llvm::sys::path::native(relative_path); + llvm::sys::path::append(clang_dir, relative_path); + if (!verify || VerifyClangPath(clang_dir)) { + LLDB_LOGF(log, + "DefaultComputeClangResourceDir: Setting ClangResourceDir " + "to \"%s\", verify = %s", + clang_dir.str().str().c_str(), verify ? "true" : "false"); + file_spec.GetDirectory().SetString(clang_dir); + FileSystem::Instance().Resolve(file_spec); + return true; + } + } + + return false; +} + +bool lldb_private::ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec, + FileSpec &file_spec, bool verify) { +#if !defined(__APPLE__) + return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec, + verify); +#else + std::string raw_path = lldb_shlib_spec.GetPath(); + + auto rev_it = llvm::sys::path::rbegin(raw_path); + auto r_end = llvm::sys::path::rend(raw_path); + + // Check for a Posix-style build of LLDB. + while (rev_it != r_end) { + if (*rev_it == "LLDB.framework") + break; + ++rev_it; + } + + // We found a non-framework build of LLDB + if (rev_it == r_end) + return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec, + verify); + + // Inside Xcode and in Xcode toolchains LLDB is always in lockstep + // with the Swift compiler, so it can reuse its Clang resource + // directory. This allows LLDB and the Swift compiler to share the + // same Clang module cache. + llvm::SmallString<256> clang_path; + const char *swift_clang_resource_dir = "usr/lib/swift/clang"; + auto parent = std::next(rev_it); + if (parent != r_end && *parent == "SharedFrameworks") { + // This is the top-level LLDB in the Xcode.app bundle. + // E.g., "Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A" + raw_path.resize(parent - r_end); + llvm::sys::path::append(clang_path, raw_path, + "Developer/Toolchains/XcodeDefault.xctoolchain", + swift_clang_resource_dir); + if (!verify || VerifyClangPath(clang_path)) { + file_spec.GetDirectory().SetString(clang_path.c_str()); + FileSystem::Instance().Resolve(file_spec); + return true; + } + } else if (parent != r_end && *parent == "PrivateFrameworks" && + std::distance(parent, r_end) > 2) { + ++parent; + ++parent; + if (*parent == "System") { + // This is LLDB inside an Xcode toolchain. + // E.g., "Xcode.app/Contents/Developer/Toolchains/" \ + // "My.xctoolchain/System/Library/PrivateFrameworks/LLDB.framework" + raw_path.resize(parent - r_end); + llvm::sys::path::append(clang_path, raw_path, swift_clang_resource_dir); + if (!verify || VerifyClangPath(clang_path)) { + file_spec.GetDirectory().SetString(clang_path.c_str()); + FileSystem::Instance().Resolve(file_spec); + return true; + } + raw_path = lldb_shlib_spec.GetPath(); + } + raw_path.resize(rev_it - r_end); + } else { + raw_path.resize(rev_it - r_end); + } + + // Fall back to the Clang resource directory inside the framework. + raw_path.append("LLDB.framework/Resources/Clang"); + file_spec.GetDirectory().SetString(raw_path.c_str()); + FileSystem::Instance().Resolve(file_spec); + return true; +#endif // __APPLE__ +} + +FileSpec lldb_private::GetClangResourceDir() { + static FileSpec g_cached_resource_dir; + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + if (FileSpec lldb_file_spec = HostInfo::GetShlibDir()) + ComputeClangResourceDirectory(lldb_file_spec, g_cached_resource_dir, + true); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + LLDB_LOGF(log, "GetClangResourceDir() => '%s'", + g_cached_resource_dir.GetPath().c_str()); + }); + return g_cached_resource_dir; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h new file mode 100644 index 00000000000..9d49188178c --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h @@ -0,0 +1,23 @@ +//===-- ClangHost.h ---------------------------------------------*- C++ -*-===// +// +// 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_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H +#define LLDB_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H + +namespace lldb_private { + +class FileSpec; + +bool ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec, + FileSpec &file_spec, bool verify); + +FileSpec GetClangResourceDir(); + +} // namespace lldb_private + +#endif diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp new file mode 100644 index 00000000000..0696c669f2e --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -0,0 +1,716 @@ +//===-- ClangModulesDeclVendor.cpp ------------------------------*- C++ -*-===// +// +// 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 <mutex> + +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Lookup.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Threading.h" + +#include "ClangHost.h" +#include "ClangModulesDeclVendor.h" +#include "ModuleDependencyCollector.h" + +#include "lldb/Core/ModuleList.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SourceModule.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; + +namespace { +// Any Clang compiler requires a consumer for diagnostics. This one stores +// them as strings so we can provide them to the user in case a module failed +// to load. +class StoringDiagnosticConsumer : public clang::DiagnosticConsumer { +public: + StoringDiagnosticConsumer(); + + void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, + const clang::Diagnostic &info) override; + + void ClearDiagnostics(); + + void DumpDiagnostics(Stream &error_stream); + +private: + typedef std::pair<clang::DiagnosticsEngine::Level, std::string> + IDAndDiagnostic; + std::vector<IDAndDiagnostic> m_diagnostics; + Log *m_log; +}; + +// The private implementation of our ClangModulesDeclVendor. Contains all the +// Clang state required to load modules. +class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor { +public: + ClangModulesDeclVendorImpl( + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine, + std::shared_ptr<clang::CompilerInvocation> compiler_invocation, + std::unique_ptr<clang::CompilerInstance> compiler_instance, + std::unique_ptr<clang::Parser> parser); + + ~ClangModulesDeclVendorImpl() override = default; + + bool AddModule(const SourceModule &module, ModuleVector *exported_modules, + Stream &error_stream) override; + + bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules, + Stream &error_stream) override; + + uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches, + std::vector<CompilerDecl> &decls) override; + + void ForEachMacro(const ModuleVector &modules, + std::function<bool(const std::string &)> handler) override; +private: + void + ReportModuleExportsHelper(std::set<ClangModulesDeclVendor::ModuleID> &exports, + clang::Module *module); + + void ReportModuleExports(ModuleVector &exports, clang::Module *module); + + clang::ModuleLoadResult DoGetModule(clang::ModuleIdPath path, + bool make_visible); + + bool m_enabled = false; + + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> m_diagnostics_engine; + std::shared_ptr<clang::CompilerInvocation> m_compiler_invocation; + std::unique_ptr<clang::CompilerInstance> m_compiler_instance; + std::unique_ptr<clang::Parser> m_parser; + size_t m_source_location_index = + 0; // used to give name components fake SourceLocations + + typedef std::vector<ConstString> ImportedModule; + typedef std::map<ImportedModule, clang::Module *> ImportedModuleMap; + typedef std::set<ModuleID> ImportedModuleSet; + ImportedModuleMap m_imported_modules; + ImportedModuleSet m_user_imported_modules; + // We assume that every ASTContext has an ClangASTContext, so we also store + // a custom ClangASTContext for our internal ASTContext. + std::unique_ptr<ClangASTContext> m_ast_context; +}; +} // anonymous namespace + +StoringDiagnosticConsumer::StoringDiagnosticConsumer() { + m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); +} + +void StoringDiagnosticConsumer::HandleDiagnostic( + clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { + llvm::SmallVector<char, 256> diagnostic_string; + + info.FormatDiagnostic(diagnostic_string); + + m_diagnostics.push_back( + IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(), + diagnostic_string.size()))); +} + +void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); } + +void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) { + for (IDAndDiagnostic &diag : m_diagnostics) { + switch (diag.first) { + default: + error_stream.PutCString(diag.second); + error_stream.PutChar('\n'); + break; + case clang::DiagnosticsEngine::Level::Ignored: + break; + } + } +} + +ClangModulesDeclVendor::ClangModulesDeclVendor() + : ClangDeclVendor(eClangModuleDeclVendor) {} + +ClangModulesDeclVendor::~ClangModulesDeclVendor() {} + +ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl( + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine, + std::shared_ptr<clang::CompilerInvocation> compiler_invocation, + std::unique_ptr<clang::CompilerInstance> compiler_instance, + std::unique_ptr<clang::Parser> parser) + : m_diagnostics_engine(std::move(diagnostics_engine)), + m_compiler_invocation(std::move(compiler_invocation)), + m_compiler_instance(std::move(compiler_instance)), + m_parser(std::move(parser)) { + + // Initialize our ClangASTContext. + m_ast_context.reset(new ClangASTContext(m_compiler_instance->getASTContext())); +} + +void ClangModulesDeclVendorImpl::ReportModuleExportsHelper( + std::set<ClangModulesDeclVendor::ModuleID> &exports, + clang::Module *module) { + if (exports.count(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module))) + return; + + exports.insert(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module)); + + llvm::SmallVector<clang::Module *, 2> sub_exports; + + module->getExportedModules(sub_exports); + + for (clang::Module *module : sub_exports) { + ReportModuleExportsHelper(exports, module); + } +} + +void ClangModulesDeclVendorImpl::ReportModuleExports( + ClangModulesDeclVendor::ModuleVector &exports, clang::Module *module) { + std::set<ClangModulesDeclVendor::ModuleID> exports_set; + + ReportModuleExportsHelper(exports_set, module); + + for (ModuleID module : exports_set) { + exports.push_back(module); + } +} + +bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, + ModuleVector *exported_modules, + Stream &error_stream) { + // Fail early. + + if (m_compiler_instance->hadModuleLoaderFatalFailure()) { + error_stream.PutCString("error: Couldn't load a module because the module " + "loader is in a fatal state.\n"); + return false; + } + + // Check if we've already imported this module. + + std::vector<ConstString> imported_module; + + for (ConstString path_component : module.path) { + imported_module.push_back(path_component); + } + + { + ImportedModuleMap::iterator mi = m_imported_modules.find(imported_module); + + if (mi != m_imported_modules.end()) { + if (exported_modules) { + ReportModuleExports(*exported_modules, mi->second); + } + return true; + } + } + + clang::HeaderSearch &HS = + m_compiler_instance->getPreprocessor().getHeaderSearchInfo(); + + if (module.search_path) { + auto path_begin = llvm::sys::path::begin(module.search_path.GetStringRef()); + auto path_end = llvm::sys::path::end(module.search_path.GetStringRef()); + auto sysroot_begin = llvm::sys::path::begin(module.sysroot.GetStringRef()); + auto sysroot_end = llvm::sys::path::end(module.sysroot.GetStringRef()); + // FIXME: Use C++14 std::equal(it, it, it, it) variant once it's available. + bool is_system_module = (std::distance(path_begin, path_end) >= + std::distance(sysroot_begin, sysroot_end)) && + std::equal(sysroot_begin, sysroot_end, path_begin); + // No need to inject search paths to modules in the sysroot. + if (!is_system_module) { + auto error = [&]() { + error_stream.Printf("error: No module map file in %s\n", + module.search_path.AsCString()); + return false; + }; + + bool is_system = true; + bool is_framework = false; + auto dir = + HS.getFileMgr().getDirectory(module.search_path.GetStringRef()); + if (!dir) + return error(); + auto *file = HS.lookupModuleMapFile(*dir, is_framework); + if (!file) + return error(); + if (!HS.loadModuleMapFile(file, is_system)) + return error(); + } + } + if (!HS.lookupModule(module.path.front().GetStringRef())) { + error_stream.Printf("error: Header search couldn't locate module %s\n", + module.path.front().AsCString()); + return false; + } + + llvm::SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>, + 4> + clang_path; + + { + clang::SourceManager &source_manager = + m_compiler_instance->getASTContext().getSourceManager(); + + for (ConstString path_component : module.path) { + clang_path.push_back(std::make_pair( + &m_compiler_instance->getASTContext().Idents.get( + path_component.GetStringRef()), + source_manager.getLocForStartOfFile(source_manager.getMainFileID()) + .getLocWithOffset(m_source_location_index++))); + } + } + + StoringDiagnosticConsumer *diagnostic_consumer = + static_cast<StoringDiagnosticConsumer *>( + m_compiler_instance->getDiagnostics().getClient()); + + diagnostic_consumer->ClearDiagnostics(); + + clang::Module *top_level_module = DoGetModule(clang_path.front(), false); + + if (!top_level_module) { + diagnostic_consumer->DumpDiagnostics(error_stream); + error_stream.Printf("error: Couldn't load top-level module %s\n", + module.path.front().AsCString()); + return false; + } + + clang::Module *submodule = top_level_module; + + for (auto &component : llvm::ArrayRef<ConstString>(module.path).drop_front()) { + submodule = submodule->findSubmodule(component.GetStringRef()); + if (!submodule) { + diagnostic_consumer->DumpDiagnostics(error_stream); + error_stream.Printf("error: Couldn't load submodule %s\n", + component.GetCString()); + return false; + } + } + + clang::Module *requested_module = DoGetModule(clang_path, true); + + if (requested_module != nullptr) { + if (exported_modules) { + ReportModuleExports(*exported_modules, requested_module); + } + + m_imported_modules[imported_module] = requested_module; + + m_enabled = true; + + return true; + } + + return false; +} + +bool ClangModulesDeclVendor::LanguageSupportsClangModules( + lldb::LanguageType language) { + switch (language) { + default: + return false; + case lldb::LanguageType::eLanguageTypeC: + case lldb::LanguageType::eLanguageTypeC11: + case lldb::LanguageType::eLanguageTypeC89: + case lldb::LanguageType::eLanguageTypeC99: + case lldb::LanguageType::eLanguageTypeC_plus_plus: + case lldb::LanguageType::eLanguageTypeC_plus_plus_03: + case lldb::LanguageType::eLanguageTypeC_plus_plus_11: + case lldb::LanguageType::eLanguageTypeC_plus_plus_14: + case lldb::LanguageType::eLanguageTypeObjC: + case lldb::LanguageType::eLanguageTypeObjC_plus_plus: + return true; + } +} + +bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit( + CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules, + Stream &error_stream) { + if (LanguageSupportsClangModules(cu.GetLanguage())) { + for (auto &imported_module : cu.GetImportedModules()) + if (!AddModule(imported_module, &exported_modules, error_stream)) + return false; + } + return true; +} + +// ClangImporter::lookupValue + +uint32_t +ClangModulesDeclVendorImpl::FindDecls(ConstString name, bool append, + uint32_t max_matches, + std::vector<CompilerDecl> &decls) { + if (!m_enabled) { + return 0; + } + + if (!append) + decls.clear(); + + clang::IdentifierInfo &ident = + m_compiler_instance->getASTContext().Idents.get(name.GetStringRef()); + + clang::LookupResult lookup_result( + m_compiler_instance->getSema(), clang::DeclarationName(&ident), + clang::SourceLocation(), clang::Sema::LookupOrdinaryName); + + m_compiler_instance->getSema().LookupName( + lookup_result, + m_compiler_instance->getSema().getScopeForContext( + m_compiler_instance->getASTContext().getTranslationUnitDecl())); + + uint32_t num_matches = 0; + + for (clang::NamedDecl *named_decl : lookup_result) { + if (num_matches >= max_matches) + return num_matches; + + decls.push_back(CompilerDecl(m_ast_context.get(), named_decl)); + ++num_matches; + } + + return num_matches; +} + +void ClangModulesDeclVendorImpl::ForEachMacro( + const ClangModulesDeclVendor::ModuleVector &modules, + std::function<bool(const std::string &)> handler) { + if (!m_enabled) { + return; + } + + typedef std::map<ModuleID, ssize_t> ModulePriorityMap; + ModulePriorityMap module_priorities; + + ssize_t priority = 0; + + for (ModuleID module : modules) { + module_priorities[module] = priority++; + } + + if (m_compiler_instance->getPreprocessor().getExternalSource()) { + m_compiler_instance->getPreprocessor() + .getExternalSource() + ->ReadDefinedMacros(); + } + + for (clang::Preprocessor::macro_iterator + mi = m_compiler_instance->getPreprocessor().macro_begin(), + me = m_compiler_instance->getPreprocessor().macro_end(); + mi != me; ++mi) { + const clang::IdentifierInfo *ii = nullptr; + + { + if (clang::IdentifierInfoLookup *lookup = + m_compiler_instance->getPreprocessor() + .getIdentifierTable() + .getExternalIdentifierLookup()) { + lookup->get(mi->first->getName()); + } + if (!ii) { + ii = mi->first; + } + } + + ssize_t found_priority = -1; + clang::MacroInfo *macro_info = nullptr; + + for (clang::ModuleMacro *module_macro : + m_compiler_instance->getPreprocessor().getLeafModuleMacros(ii)) { + clang::Module *module = module_macro->getOwningModule(); + + { + ModulePriorityMap::iterator pi = + module_priorities.find(reinterpret_cast<ModuleID>(module)); + + if (pi != module_priorities.end() && pi->second > found_priority) { + macro_info = module_macro->getMacroInfo(); + found_priority = pi->second; + } + } + + clang::Module *top_level_module = module->getTopLevelModule(); + + if (top_level_module != module) { + ModulePriorityMap::iterator pi = module_priorities.find( + reinterpret_cast<ModuleID>(top_level_module)); + + if ((pi != module_priorities.end()) && pi->second > found_priority) { + macro_info = module_macro->getMacroInfo(); + found_priority = pi->second; + } + } + } + + if (macro_info) { + std::string macro_expansion = "#define "; + macro_expansion.append(mi->first->getName().str()); + + { + if (macro_info->isFunctionLike()) { + macro_expansion.append("("); + + bool first_arg = true; + + for (auto pi = macro_info->param_begin(), + pe = macro_info->param_end(); + pi != pe; ++pi) { + if (!first_arg) { + macro_expansion.append(", "); + } else { + first_arg = false; + } + + macro_expansion.append((*pi)->getName().str()); + } + + if (macro_info->isC99Varargs()) { + if (first_arg) { + macro_expansion.append("..."); + } else { + macro_expansion.append(", ..."); + } + } else if (macro_info->isGNUVarargs()) { + macro_expansion.append("..."); + } + + macro_expansion.append(")"); + } + + macro_expansion.append(" "); + + bool first_token = true; + + for (clang::MacroInfo::tokens_iterator ti = macro_info->tokens_begin(), + te = macro_info->tokens_end(); + ti != te; ++ti) { + if (!first_token) { + macro_expansion.append(" "); + } else { + first_token = false; + } + + if (ti->isLiteral()) { + if (const char *literal_data = ti->getLiteralData()) { + std::string token_str(literal_data, ti->getLength()); + macro_expansion.append(token_str); + } else { + bool invalid = false; + const char *literal_source = + m_compiler_instance->getSourceManager().getCharacterData( + ti->getLocation(), &invalid); + + if (invalid) { + lldbassert(0 && "Unhandled token kind"); + macro_expansion.append("<unknown literal value>"); + } else { + macro_expansion.append( + std::string(literal_source, ti->getLength())); + } + } + } else if (const char *punctuator_spelling = + clang::tok::getPunctuatorSpelling(ti->getKind())) { + macro_expansion.append(punctuator_spelling); + } else if (const char *keyword_spelling = + clang::tok::getKeywordSpelling(ti->getKind())) { + macro_expansion.append(keyword_spelling); + } else { + switch (ti->getKind()) { + case clang::tok::TokenKind::identifier: + macro_expansion.append(ti->getIdentifierInfo()->getName().str()); + break; + case clang::tok::TokenKind::raw_identifier: + macro_expansion.append(ti->getRawIdentifier().str()); + break; + default: + macro_expansion.append(ti->getName()); + break; + } + } + } + + if (handler(macro_expansion)) { + return; + } + } + } + } +} + +clang::ModuleLoadResult +ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path, + bool make_visible) { + clang::Module::NameVisibilityKind visibility = + make_visible ? clang::Module::AllVisible : clang::Module::Hidden; + + const bool is_inclusion_directive = false; + + return m_compiler_instance->loadModule(path.front().second, path, visibility, + is_inclusion_directive); +} + +static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer"; + +lldb_private::ClangModulesDeclVendor * +ClangModulesDeclVendor::Create(Target &target) { + // FIXME we should insure programmatically that the expression parser's + // compiler and the modules runtime's + // compiler are both initialized in the same way – preferably by the same + // code. + + if (!target.GetPlatform()->SupportsModules()) + return nullptr; + + const ArchSpec &arch = target.GetArchitecture(); + + std::vector<std::string> compiler_invocation_arguments = { + "clang", + "-fmodules", + "-fimplicit-module-maps", + "-fcxx-modules", + "-fsyntax-only", + "-femit-all-decls", + "-target", + arch.GetTriple().str(), + "-fmodules-validate-system-headers", + "-Werror=non-modular-include-in-framework-module"}; + + target.GetPlatform()->AddClangModuleCompilationOptions( + &target, compiler_invocation_arguments); + + compiler_invocation_arguments.push_back(ModuleImportBufferName); + + // Add additional search paths with { "-I", path } or { "-F", path } here. + + { + llvm::SmallString<128> path; + auto props = ModuleList::GetGlobalModuleListProperties(); + props.GetClangModulesCachePath().GetPath(path); + std::string module_cache_argument("-fmodules-cache-path="); + module_cache_argument.append(path.str()); + compiler_invocation_arguments.push_back(module_cache_argument); + } + + FileSpecList module_search_paths = target.GetClangModuleSearchPaths(); + + for (size_t spi = 0, spe = module_search_paths.GetSize(); spi < spe; ++spi) { + const FileSpec &search_path = module_search_paths.GetFileSpecAtIndex(spi); + + std::string search_path_argument = "-I"; + search_path_argument.append(search_path.GetPath()); + + compiler_invocation_arguments.push_back(search_path_argument); + } + + { + FileSpec clang_resource_dir = GetClangResourceDir(); + + if (FileSystem::Instance().IsDirectory(clang_resource_dir.GetPath())) { + compiler_invocation_arguments.push_back("-resource-dir"); + compiler_invocation_arguments.push_back(clang_resource_dir.GetPath()); + } + } + + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine = + clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions, + new StoringDiagnosticConsumer); + + std::vector<const char *> compiler_invocation_argument_cstrs; + compiler_invocation_argument_cstrs.reserve( + compiler_invocation_arguments.size()); + for (const std::string &arg : compiler_invocation_arguments) + compiler_invocation_argument_cstrs.push_back(arg.c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + LLDB_LOG(log, "ClangModulesDeclVendor's compiler flags {0:$[ ]}", + llvm::make_range(compiler_invocation_arguments.begin(), + compiler_invocation_arguments.end())); + + std::shared_ptr<clang::CompilerInvocation> invocation = + clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs, + diagnostics_engine); + + if (!invocation) + return nullptr; + + std::unique_ptr<llvm::MemoryBuffer> source_buffer = + llvm::MemoryBuffer::getMemBuffer( + "extern int __lldb __attribute__((unavailable));", + ModuleImportBufferName); + + invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName, + source_buffer.release()); + + std::unique_ptr<clang::CompilerInstance> instance( + new clang::CompilerInstance); + + // When capturing a reproducer, hook up the file collector with clang to + // collector modules and headers. + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { + repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>(); + instance->setModuleDepCollector( + std::make_shared<ModuleDependencyCollectorAdaptor>( + fp.GetFileCollector())); + clang::DependencyOutputOptions &opts = instance->getDependencyOutputOpts(); + opts.IncludeSystemHeaders = true; + opts.IncludeModuleFiles = true; + } + + // Make sure clang uses the same VFS as LLDB. + instance->createFileManager(FileSystem::Instance().GetVirtualFileSystem()); + instance->setDiagnostics(diagnostics_engine.get()); + instance->setInvocation(invocation); + + std::unique_ptr<clang::FrontendAction> action(new clang::SyntaxOnlyAction); + + instance->setTarget(clang::TargetInfo::CreateTargetInfo( + *diagnostics_engine, instance->getInvocation().TargetOpts)); + + if (!instance->hasTarget()) + return nullptr; + + instance->getTarget().adjust(instance->getLangOpts()); + + if (!action->BeginSourceFile(*instance, + instance->getFrontendOpts().Inputs[0])) + return nullptr; + + instance->getPreprocessor().enableIncrementalProcessing(); + + instance->createASTReader(); + + instance->createSema(action->getTranslationUnitKind(), nullptr); + + const bool skipFunctionBodies = false; + std::unique_ptr<clang::Parser> parser(new clang::Parser( + instance->getPreprocessor(), instance->getSema(), skipFunctionBodies)); + + instance->getPreprocessor().EnterMainSourceFile(); + parser->Initialize(); + + clang::Parser::DeclGroupPtrTy parsed; + + while (!parser->ParseTopLevelDecl(parsed)) + ; + + return new ClangModulesDeclVendorImpl(std::move(diagnostics_engine), + std::move(invocation), + std::move(instance), std::move(parser)); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h new file mode 100644 index 00000000000..e099b59041d --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h @@ -0,0 +1,116 @@ +//===-- ClangModulesDeclVendor.h --------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangModulesDeclVendor_h +#define liblldb_ClangModulesDeclVendor_h + +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/SourceModule.h" +#include "lldb/Target/Platform.h" + +#include "Plugins/ExpressionParser/Clang/ClangDeclVendor.h" + +#include <set> +#include <vector> + +namespace lldb_private { + +class ClangModulesDeclVendor : public ClangDeclVendor { +public: + // Constructors and Destructors + ClangModulesDeclVendor(); + + ~ClangModulesDeclVendor() override; + + static bool classof(const DeclVendor *vendor) { + return vendor->GetKind() == eClangModuleDeclVendor; + } + + static ClangModulesDeclVendor *Create(Target &target); + + typedef std::vector<ConstString> ModulePath; + typedef uintptr_t ModuleID; + typedef std::vector<ModuleID> ModuleVector; + + /// Add a module to the list of modules to search. + /// + /// \param[in] module + /// The path to the exact module to be loaded. E.g., if the desired + /// module is std.io, then this should be { "std", "io" }. + /// + /// \param[in] exported_modules + /// If non-NULL, a pointer to a vector to populate with the ID of every + /// module that is re-exported by the specified module. + /// + /// \param[in] error_stream + /// A stream to populate with the output of the Clang parser when + /// it tries to load the module. + /// + /// \return + /// True if the module could be loaded; false if not. If the + /// compiler encountered a fatal error during a previous module + /// load, then this will always return false for this ModuleImporter. + virtual bool AddModule(const SourceModule &module, + ModuleVector *exported_modules, + Stream &error_stream) = 0; + + /// Add all modules referred to in a given compilation unit to the list + /// of modules to search. + /// + /// \param[in] cu + /// The compilation unit to scan for imported modules. + /// + /// \param[in] exported_modules + /// A vector to populate with the ID of each module loaded (directly + /// and via re-exports) in this way. + /// + /// \param[in] error_stream + /// A stream to populate with the output of the Clang parser when + /// it tries to load the modules. + /// + /// \return + /// True if all modules referred to by the compilation unit could be + /// loaded; false if one could not be loaded. If the compiler + /// encountered a fatal error during a previous module + /// load, then this will always return false for this ModuleImporter. + virtual bool AddModulesForCompileUnit(CompileUnit &cu, + ModuleVector &exported_modules, + Stream &error_stream) = 0; + + /// Enumerate all the macros that are defined by a given set of modules + /// that are already imported. + /// + /// \param[in] modules + /// The unique IDs for all modules to query. Later modules have higher + /// priority, just as if you @imported them in that order. This matters + /// if module A #defines a macro and module B #undefs it. + /// + /// \param[in] handler + /// A function to call with the text of each #define (including the + /// #define directive). #undef directives are not included; we simply + /// elide any corresponding #define. If this function returns true, + /// we stop the iteration immediately. + virtual void + ForEachMacro(const ModuleVector &modules, + std::function<bool(const std::string &)> handler) = 0; + + /// Query whether Clang supports modules for a particular language. + /// LLDB uses this to decide whether to try to find the modules loaded + /// by a given compile unit. + /// + /// \param[in] language + /// The language to query for. + /// + /// \return + /// True if Clang has modules for the given language. + static bool LanguageSupportsClangModules(lldb::LanguageType language); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangModulesDeclVendor_h diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp new file mode 100644 index 00000000000..41d62a462ab --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp @@ -0,0 +1,101 @@ +//===-- ClangPersistentVariables.cpp ----------------------------*- C++ -*-===// +// +// 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 "ClangPersistentVariables.h" + +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +#include "clang/AST/Decl.h" + +#include "llvm/ADT/StringMap.h" + +using namespace lldb; +using namespace lldb_private; + +ClangPersistentVariables::ClangPersistentVariables() + : lldb_private::PersistentExpressionState(LLVMCastKind::eKindClang) {} + +ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable( + const lldb::ValueObjectSP &valobj_sp) { + return AddNewlyConstructedVariable(new ClangExpressionVariable(valobj_sp)); +} + +ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable( + ExecutionContextScope *exe_scope, ConstString name, + const CompilerType &compiler_type, lldb::ByteOrder byte_order, + uint32_t addr_byte_size) { + return AddNewlyConstructedVariable(new ClangExpressionVariable( + exe_scope, name, compiler_type, byte_order, addr_byte_size)); +} + +void ClangPersistentVariables::RemovePersistentVariable( + lldb::ExpressionVariableSP variable) { + RemoveVariable(variable); + + // Check if the removed variable was the last one that was created. If yes, + // reuse the variable id for the next variable. + + // Nothing to do if we have not assigned a variable id so far. + if (m_next_persistent_variable_id == 0) + return; + + llvm::StringRef name = variable->GetName().GetStringRef(); + // Remove the prefix from the variable that only the indes is left. + if (!name.consume_front(GetPersistentVariablePrefix(false))) + return; + + // Check if the variable contained a variable id. + uint32_t variable_id; + if (name.getAsInteger(10, variable_id)) + return; + // If it's the most recent variable id that was assigned, make sure that this + // variable id will be used for the next persistent variable. + if (variable_id == m_next_persistent_variable_id - 1) + m_next_persistent_variable_id--; +} + +llvm::Optional<CompilerType> +ClangPersistentVariables::GetCompilerTypeFromPersistentDecl( + ConstString type_name) { + PersistentDecl p = m_persistent_decls.lookup(type_name.GetCString()); + + if (p.m_decl == nullptr) + return llvm::None; + + if (clang::TypeDecl *tdecl = llvm::dyn_cast<clang::TypeDecl>(p.m_decl)) { + opaque_compiler_type_t t = static_cast<opaque_compiler_type_t>( + const_cast<clang::Type *>(tdecl->getTypeForDecl())); + return CompilerType(p.m_context, t); + } + return llvm::None; +} + +void ClangPersistentVariables::RegisterPersistentDecl(ConstString name, + clang::NamedDecl *decl, + ClangASTContext *ctx) { + PersistentDecl p = {decl, ctx}; + m_persistent_decls.insert(std::make_pair(name.GetCString(), p)); + + if (clang::EnumDecl *enum_decl = llvm::dyn_cast<clang::EnumDecl>(decl)) { + for (clang::EnumConstantDecl *enumerator_decl : enum_decl->enumerators()) { + p = {enumerator_decl, ctx}; + m_persistent_decls.insert(std::make_pair( + ConstString(enumerator_decl->getNameAsString()).GetCString(), p)); + } + } +} + +clang::NamedDecl * +ClangPersistentVariables::GetPersistentDecl(ConstString name) { + return m_persistent_decls.lookup(name.GetCString()).m_decl; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h new file mode 100644 index 00000000000..434196b35fd --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h @@ -0,0 +1,103 @@ +//===-- ClangPersistentVariables.h ------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangPersistentVariables_h_ +#define liblldb_ClangPersistentVariables_h_ + +#include "llvm/ADT/DenseMap.h" + +#include "ClangExpressionVariable.h" +#include "ClangModulesDeclVendor.h" + +#include "lldb/Expression/ExpressionVariable.h" + +namespace lldb_private { + +/// \class ClangPersistentVariables ClangPersistentVariables.h +/// "lldb/Expression/ClangPersistentVariables.h" Manages persistent values +/// that need to be preserved between expression invocations. +/// +/// A list of variables that can be accessed and updated by any expression. See +/// ClangPersistentVariable for more discussion. Also provides an increasing, +/// 0-based counter for naming result variables. +class ClangPersistentVariables : public PersistentExpressionState { +public: + ClangPersistentVariables(); + + ~ClangPersistentVariables() override = default; + + // llvm casting support + static bool classof(const PersistentExpressionState *pv) { + return pv->getKind() == PersistentExpressionState::eKindClang; + } + + lldb::ExpressionVariableSP + CreatePersistentVariable(const lldb::ValueObjectSP &valobj_sp) override; + + lldb::ExpressionVariableSP CreatePersistentVariable( + ExecutionContextScope *exe_scope, ConstString name, + const CompilerType &compiler_type, lldb::ByteOrder byte_order, + uint32_t addr_byte_size) override; + + void RemovePersistentVariable(lldb::ExpressionVariableSP variable) override; + + llvm::StringRef GetPersistentVariablePrefix(bool is_error) const override { + return "$"; + } + + /// Returns the next file name that should be used for user expressions. + std::string GetNextExprFileName() { + std::string name; + name.append("<user expression "); + name.append(std::to_string(m_next_user_file_id++)); + name.append(">"); + return name; + } + + llvm::Optional<CompilerType> + GetCompilerTypeFromPersistentDecl(ConstString type_name) override; + + void RegisterPersistentDecl(ConstString name, clang::NamedDecl *decl, + ClangASTContext *ctx); + + clang::NamedDecl *GetPersistentDecl(ConstString name); + + void AddHandLoadedClangModule(ClangModulesDeclVendor::ModuleID module) { + m_hand_loaded_clang_modules.push_back(module); + } + + const ClangModulesDeclVendor::ModuleVector &GetHandLoadedClangModules() { + return m_hand_loaded_clang_modules; + } + +private: + /// The counter used by GetNextExprFileName. + uint32_t m_next_user_file_id = 0; + // The counter used by GetNextPersistentVariableName + uint32_t m_next_persistent_variable_id = 0; + + struct PersistentDecl { + /// The persistent decl. + clang::NamedDecl *m_decl = nullptr; + /// The ClangASTContext for the ASTContext of m_decl. + ClangASTContext *m_context = nullptr; + }; + + typedef llvm::DenseMap<const char *, PersistentDecl> PersistentDeclMap; + PersistentDeclMap + m_persistent_decls; ///< Persistent entities declared by the user. + + ClangModulesDeclVendor::ModuleVector + m_hand_loaded_clang_modules; ///< These are Clang modules we hand-loaded; + ///these are the highest- + ///< priority source for macros. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangPersistentVariables_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp new file mode 100644 index 00000000000..6698797617a --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -0,0 +1,937 @@ +//===-- ClangUserExpression.cpp ---------------------------------*- C++ -*-===// +// +// 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 "lldb/Host/Config.h" + +#include <stdio.h> +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <cstdlib> +#include <map> +#include <string> + +#include "ClangUserExpression.h" + +#include "ASTResultSynthesizer.h" +#include "ClangDiagnostic.h" +#include "ClangExpressionDeclMap.h" +#include "ClangExpressionParser.h" +#include "ClangModulesDeclVendor.h" +#include "ClangPersistentVariables.h" +#include "CppModuleConfiguration.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTMetadata.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallUserExpression.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +#include "llvm/ADT/ScopeExit.h" + +using namespace lldb_private; + +char ClangUserExpression::ID; + +ClangUserExpression::ClangUserExpression( + ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, lldb::LanguageType language, + ResultType desired_type, const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) + : LLVMUserExpression(exe_scope, expr, prefix, language, desired_type, + options), + m_type_system_helper(*m_target_wp.lock(), options.GetExecutionPolicy() == + eExecutionPolicyTopLevel), + m_result_delegate(exe_scope.CalculateTarget()), m_ctx_obj(ctx_obj) { + switch (m_language) { + case lldb::eLanguageTypeC_plus_plus: + m_allow_cxx = true; + break; + case lldb::eLanguageTypeObjC: + m_allow_objc = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_allow_cxx = true; + m_allow_objc = true; + break; + } +} + +ClangUserExpression::~ClangUserExpression() {} + +void ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOGF(log, "ClangUserExpression::ScanContext()"); + + m_target = exe_ctx.GetTargetPtr(); + + if (!(m_allow_cxx || m_allow_objc)) { + LLDB_LOGF(log, " [CUE::SC] Settings inhibit C++ and Objective-C"); + return; + } + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == nullptr) { + LLDB_LOGF(log, " [CUE::SC] Null stack frame"); + return; + } + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + + if (!sym_ctx.function) { + LLDB_LOGF(log, " [CUE::SC] Null function"); + return; + } + + // Find the block that defines the function represented by "sym_ctx" + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) { + LLDB_LOGF(log, " [CUE::SC] Null function block"); + return; + } + + CompilerDeclContext decl_context = function_block->GetDeclContext(); + + if (!decl_context) { + LLDB_LOGF(log, " [CUE::SC] Null decl context"); + return; + } + + if (m_ctx_obj) { + switch (m_ctx_obj->GetObjectRuntimeLanguage()) { + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC89: + case lldb::eLanguageTypeC99: + case lldb::eLanguageTypeC11: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + m_in_cplusplus_method = true; + break; + case lldb::eLanguageTypeObjC: + case lldb::eLanguageTypeObjC_plus_plus: + m_in_objectivec_method = true; + break; + default: + break; + } + m_needs_object_ptr = true; + } else if (clang::CXXMethodDecl *method_decl = + ClangASTContext::DeclContextGetAsCXXMethodDecl(decl_context)) { + if (m_allow_cxx && method_decl->isInstance()) { + if (m_enforce_valid_object) { + lldb::VariableListSP variable_list_sp( + function_block->GetBlockVariableList(true)); + + const char *thisErrorString = "Stopped in a C++ method, but 'this' " + "isn't available; pretending we are in a " + "generic context"; + + if (!variable_list_sp) { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp( + variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame(frame)) { + err.SetErrorString(thisErrorString); + return; + } + } + + m_in_cplusplus_method = true; + m_needs_object_ptr = true; + } + } else if (clang::ObjCMethodDecl *method_decl = + ClangASTContext::DeclContextGetAsObjCMethodDecl( + decl_context)) { + if (m_allow_objc) { + if (m_enforce_valid_object) { + lldb::VariableListSP variable_list_sp( + function_block->GetBlockVariableList(true)); + + const char *selfErrorString = "Stopped in an Objective-C method, but " + "'self' isn't available; pretending we " + "are in a generic context"; + + if (!variable_list_sp) { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = + variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame(frame)) { + err.SetErrorString(selfErrorString); + return; + } + } + + m_in_objectivec_method = true; + m_needs_object_ptr = true; + + if (!method_decl->isInstanceMethod()) + m_in_static_method = true; + } + } else if (clang::FunctionDecl *function_decl = + ClangASTContext::DeclContextGetAsFunctionDecl(decl_context)) { + // We might also have a function that said in the debug information that it + // captured an object pointer. The best way to deal with getting to the + // ivars at present is by pretending that this is a method of a class in + // whatever runtime the debug info says the object pointer belongs to. Do + // that here. + + ClangASTMetadata *metadata = + ClangASTContext::DeclContextGetMetaData(decl_context, function_decl); + if (metadata && metadata->HasObjectPtr()) { + lldb::LanguageType language = metadata->GetObjectPtrLanguage(); + if (language == lldb::eLanguageTypeC_plus_plus) { + if (m_enforce_valid_object) { + lldb::VariableListSP variable_list_sp( + function_block->GetBlockVariableList(true)); + + const char *thisErrorString = "Stopped in a context claiming to " + "capture a C++ object pointer, but " + "'this' isn't available; pretending we " + "are in a generic context"; + + if (!variable_list_sp) { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp( + variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame(frame)) { + err.SetErrorString(thisErrorString); + return; + } + } + + m_in_cplusplus_method = true; + m_needs_object_ptr = true; + } else if (language == lldb::eLanguageTypeObjC) { + if (m_enforce_valid_object) { + lldb::VariableListSP variable_list_sp( + function_block->GetBlockVariableList(true)); + + const char *selfErrorString = + "Stopped in a context claiming to capture an Objective-C object " + "pointer, but 'self' isn't available; pretending we are in a " + "generic context"; + + if (!variable_list_sp) { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = + variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame(frame)) { + err.SetErrorString(selfErrorString); + return; + } + + Type *self_type = self_variable_sp->GetType(); + + if (!self_type) { + err.SetErrorString(selfErrorString); + return; + } + + CompilerType self_clang_type = self_type->GetForwardCompilerType(); + + if (!self_clang_type) { + err.SetErrorString(selfErrorString); + return; + } + + if (ClangASTContext::IsObjCClassType(self_clang_type)) { + return; + } else if (ClangASTContext::IsObjCObjectPointerType( + self_clang_type)) { + m_in_objectivec_method = true; + m_needs_object_ptr = true; + } else { + err.SetErrorString(selfErrorString); + return; + } + } else { + m_in_objectivec_method = true; + m_needs_object_ptr = true; + } + } + } + } +} + +// This is a really nasty hack, meant to fix Objective-C expressions of the +// form (int)[myArray count]. Right now, because the type information for +// count is not available, [myArray count] returns id, which can't be directly +// cast to int without causing a clang error. +static void ApplyObjcCastHack(std::string &expr) { + const std::string from = "(int)["; + const std::string to = "(int)(long long)["; + + size_t offset; + + while ((offset = expr.find(from)) != expr.npos) + expr.replace(offset, from.size(), to); +} + +bool ClangUserExpression::SetupPersistentState(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx) { + if (Target *target = exe_ctx.GetTargetPtr()) { + if (PersistentExpressionState *persistent_state = + target->GetPersistentExpressionStateForLanguage( + lldb::eLanguageTypeC)) { + m_clang_state = llvm::cast<ClangPersistentVariables>(persistent_state); + m_result_delegate.RegisterPersistentState(persistent_state); + } else { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "couldn't start parsing (no persistent data)"); + return false; + } + } else { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "error: couldn't start parsing (no target)"); + return false; + } + return true; +} + +static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target) { + if (ClangModulesDeclVendor *decl_vendor = + target->GetClangModulesDeclVendor()) { + auto *persistent_state = llvm::cast<ClangPersistentVariables>( + target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); + if (!persistent_state) + return; + const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = + persistent_state->GetHandLoadedClangModules(); + ClangModulesDeclVendor::ModuleVector modules_for_macros; + + for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) { + modules_for_macros.push_back(module); + } + + if (target->GetEnableAutoImportClangModules()) { + if (StackFrame *frame = exe_ctx.GetFramePtr()) { + if (Block *block = frame->GetFrameBlock()) { + SymbolContext sc; + + block->CalculateSymbolContext(&sc); + + if (sc.comp_unit) { + StreamString error_stream; + + decl_vendor->AddModulesForCompileUnit( + *sc.comp_unit, modules_for_macros, error_stream); + } + } + } + } + } +} + +void ClangUserExpression::UpdateLanguageForExpr() { + m_expr_lang = lldb::LanguageType::eLanguageTypeUnknown; + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) + return; + if (m_in_cplusplus_method) + m_expr_lang = lldb::eLanguageTypeC_plus_plus; + else if (m_in_objectivec_method) + m_expr_lang = lldb::eLanguageTypeObjC; + else + m_expr_lang = lldb::eLanguageTypeC; +} + +void ClangUserExpression::CreateSourceCode( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + std::vector<std::string> modules_to_import, bool for_completion) { + + m_filename = m_clang_state->GetNextExprFileName(); + std::string prefix = m_expr_prefix; + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + m_transformed_text = m_expr_text; + } else { + m_source_code.reset(ClangExpressionSourceCode::CreateWrapped( + m_filename, prefix.c_str(), m_expr_text.c_str())); + + if (!m_source_code->GetText(m_transformed_text, m_expr_lang, + m_in_static_method, exe_ctx, !m_ctx_obj, + for_completion, modules_to_import)) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "couldn't construct expression body"); + return; + } + + // Find and store the start position of the original code inside the + // transformed code. We need this later for the code completion. + std::size_t original_start; + std::size_t original_end; + bool found_bounds = m_source_code->GetOriginalBodyBounds( + m_transformed_text, m_expr_lang, original_start, original_end); + if (found_bounds) + m_user_expression_start_pos = original_start; + } +} + +static bool SupportsCxxModuleImport(lldb::LanguageType language) { + switch (language) { + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + case lldb::eLanguageTypeObjC_plus_plus: + return true; + default: + return false; + } +} + +/// Utility method that puts a message into the expression log and +/// returns an invalid module configuration. +static CppModuleConfiguration LogConfigError(const std::string &msg) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + LLDB_LOG(log, "[C++ module config] {0}", msg); + return CppModuleConfiguration(); +} + +CppModuleConfiguration GetModuleConfig(lldb::LanguageType language, + ExecutionContext &exe_ctx) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + // Don't do anything if this is not a C++ module configuration. + if (!SupportsCxxModuleImport(language)) + return LogConfigError("Language doesn't support C++ modules"); + + Target *target = exe_ctx.GetTargetPtr(); + if (!target) + return LogConfigError("No target"); + + if (!target->GetEnableImportStdModule()) + return LogConfigError("Importing std module not enabled in settings"); + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) + return LogConfigError("No frame"); + + Block *block = frame->GetFrameBlock(); + if (!block) + return LogConfigError("No block"); + + SymbolContext sc; + block->CalculateSymbolContext(&sc); + if (!sc.comp_unit) + return LogConfigError("Couldn't calculate symbol context"); + + // Build a list of files we need to analyze to build the configuration. + FileSpecList files; + for (const FileSpec &f : sc.comp_unit->GetSupportFiles()) + files.AppendIfUnique(f); + // We also need to look at external modules in the case of -gmodules as they + // contain the support files for libc++ and the C library. + llvm::DenseSet<SymbolFile *> visited_symbol_files; + sc.comp_unit->ForEachExternalModule( + visited_symbol_files, [&files](Module &module) { + for (std::size_t i = 0; i < module.GetNumCompileUnits(); ++i) { + const FileSpecList &support_files = + module.GetCompileUnitAtIndex(i)->GetSupportFiles(); + for (const FileSpec &f : support_files) { + files.AppendIfUnique(f); + } + } + return false; + }); + + LLDB_LOG(log, "[C++ module config] Found {0} support files to analyze", + files.GetSize()); + if (log && log->GetVerbose()) { + for (const FileSpec &f : files) + LLDB_LOGV(log, "[C++ module config] Analyzing support file: {0}", + f.GetPath()); + } + + // Try to create a configuration from the files. If there is no valid + // configuration possible with the files, this just returns an invalid + // configuration. + return CppModuleConfiguration(files); +} + +bool ClangUserExpression::PrepareForParsing( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + bool for_completion) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + InstallContext(exe_ctx); + + if (!SetupPersistentState(diagnostic_manager, exe_ctx)) + return false; + + Status err; + ScanContext(exe_ctx, err); + + if (!err.Success()) { + diagnostic_manager.PutString(eDiagnosticSeverityWarning, err.AsCString()); + } + + //////////////////////////////////// + // Generate the expression + // + + ApplyObjcCastHack(m_expr_text); + + SetupDeclVendor(exe_ctx, m_target); + + CppModuleConfiguration module_config = GetModuleConfig(m_language, exe_ctx); + llvm::ArrayRef<std::string> imported_modules = + module_config.GetImportedModules(); + m_imported_cpp_modules = !imported_modules.empty(); + m_include_directories = module_config.GetIncludeDirs(); + + LLDB_LOG(log, "List of imported modules in expression: {0}", + llvm::make_range(imported_modules.begin(), imported_modules.end())); + LLDB_LOG(log, "List of include directories gathered for modules: {0}", + llvm::make_range(m_include_directories.begin(), + m_include_directories.end())); + + UpdateLanguageForExpr(); + CreateSourceCode(diagnostic_manager, exe_ctx, imported_modules, + for_completion); + return true; +} + +bool ClangUserExpression::Parse(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory, + bool generate_debug_info) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ false)) + return false; + + LLDB_LOGF(log, "Parsing the following code:\n%s", m_transformed_text.c_str()); + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) { + diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + m_materializer_up.reset(new Materializer()); + + ResetDeclMap(exe_ctx, m_result_delegate, keep_result_in_memory); + + auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); }); + + if (!DeclMap()->WillParse(exe_ctx, GetMaterializer())) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "current process state is unsuitable for expression parsing"); + return false; + } + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + DeclMap()->SetLookupsEnabled(true); + } + + Process *process = exe_ctx.GetProcessPtr(); + ExecutionContextScope *exe_scope = process; + + if (!exe_scope) + exe_scope = exe_ctx.GetTargetPtr(); + + // We use a shared pointer here so we can use the original parser - if it + // succeeds or the rewrite parser we might make if it fails. But the + // parser_sp will never be empty. + + ClangExpressionParser parser(exe_scope, *this, generate_debug_info, + m_include_directories, m_filename); + + unsigned num_errors = parser.Parse(diagnostic_manager); + + // Check here for FixItHints. If there are any try to apply the fixits and + // set the fixed text in m_fixed_text before returning an error. + if (num_errors) { + if (diagnostic_manager.HasFixIts()) { + if (parser.RewriteExpression(diagnostic_manager)) { + size_t fixed_start; + size_t fixed_end; + const std::string &fixed_expression = + diagnostic_manager.GetFixedExpression(); + // Retrieve the original expression in case we don't have a top level + // expression (which has no surrounding source code). + if (m_source_code && + m_source_code->GetOriginalBodyBounds(fixed_expression, m_expr_lang, + fixed_start, fixed_end)) + m_fixed_text = + fixed_expression.substr(fixed_start, fixed_end - fixed_start); + } + } + return false; + } + + ////////////////////////////////////////////////////////////////////////////// + // Prepare the output of the parser for execution, evaluating it statically + // if possible + // + + { + Status jit_error = parser.PrepareForExecution( + m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx, + m_can_interpret, execution_policy); + + if (!jit_error.Success()) { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + diagnostic_manager.PutString(eDiagnosticSeverityError, error_cstr); + else + diagnostic_manager.PutString(eDiagnosticSeverityError, + "expression can't be interpreted or run"); + return false; + } + } + + if (exe_ctx.GetProcessPtr() && execution_policy == eExecutionPolicyTopLevel) { + Status static_init_error = + parser.RunStaticInitializers(m_execution_unit_sp, exe_ctx); + + if (!static_init_error.Success()) { + const char *error_cstr = static_init_error.AsCString(); + if (error_cstr && error_cstr[0]) + diagnostic_manager.Printf(eDiagnosticSeverityError, + "couldn't run static initializers: %s\n", + error_cstr); + else + diagnostic_manager.PutString(eDiagnosticSeverityError, + "couldn't run static initializers\n"); + return false; + } + } + + if (m_execution_unit_sp) { + bool register_execution_unit = false; + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + register_execution_unit = true; + } + + // If there is more than one external function in the execution unit, it + // needs to keep living even if it's not top level, because the result + // could refer to that function. + + if (m_execution_unit_sp->GetJittedFunctions().size() > 1) { + register_execution_unit = true; + } + + if (register_execution_unit) { + if (auto *persistent_state = + exe_ctx.GetTargetPtr()->GetPersistentExpressionStateForLanguage( + m_language)) + persistent_state->RegisterExecutionUnit(m_execution_unit_sp); + } + } + + if (generate_debug_info) { + lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule()); + + if (jit_module_sp) { + ConstString const_func_name(FunctionName()); + FileSpec jit_file; + jit_file.GetFilename() = const_func_name; + jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString()); + m_jit_module_wp = jit_module_sp; + target->GetImages().Append(jit_module_sp); + } + } + + if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + return true; +} + +/// Converts an absolute position inside a given code string into +/// a column/line pair. +/// +/// \param[in] abs_pos +/// A absolute position in the code string that we want to convert +/// to a column/line pair. +/// +/// \param[in] code +/// A multi-line string usually representing source code. +/// +/// \param[out] line +/// The line in the code that contains the given absolute position. +/// The first line in the string is indexed as 1. +/// +/// \param[out] column +/// The column in the line that contains the absolute position. +/// The first character in a line is indexed as 0. +static void AbsPosToLineColumnPos(size_t abs_pos, llvm::StringRef code, + unsigned &line, unsigned &column) { + // Reset to code position to beginning of the file. + line = 0; + column = 0; + + assert(abs_pos <= code.size() && "Absolute position outside code string?"); + + // We have to walk up to the position and count lines/columns. + for (std::size_t i = 0; i < abs_pos; ++i) { + // If we hit a line break, we go back to column 0 and enter a new line. + // We only handle \n because that's what we internally use to make new + // lines for our temporary code strings. + if (code[i] == '\n') { + ++line; + column = 0; + continue; + } + ++column; + } +} + +bool ClangUserExpression::Complete(ExecutionContext &exe_ctx, + CompletionRequest &request, + unsigned complete_pos) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + // We don't want any visible feedback when completing an expression. Mostly + // because the results we get from an incomplete invocation are probably not + // correct. + DiagnosticManager diagnostic_manager; + + if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ true)) + return false; + + LLDB_LOGF(log, "Parsing the following code:\n%s", m_transformed_text.c_str()); + + ////////////////////////// + // Parse the expression + // + + m_materializer_up.reset(new Materializer()); + + ResetDeclMap(exe_ctx, m_result_delegate, /*keep result in memory*/ true); + + auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); }); + + if (!DeclMap()->WillParse(exe_ctx, GetMaterializer())) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "current process state is unsuitable for expression parsing"); + + return false; + } + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + DeclMap()->SetLookupsEnabled(true); + } + + Process *process = exe_ctx.GetProcessPtr(); + ExecutionContextScope *exe_scope = process; + + if (!exe_scope) + exe_scope = exe_ctx.GetTargetPtr(); + + ClangExpressionParser parser(exe_scope, *this, false); + + // We have to find the source code location where the user text is inside + // the transformed expression code. When creating the transformed text, we + // already stored the absolute position in the m_transformed_text string. The + // only thing left to do is to transform it into the line:column format that + // Clang expects. + + // The line and column of the user expression inside the transformed source + // code. + unsigned user_expr_line, user_expr_column; + if (m_user_expression_start_pos.hasValue()) + AbsPosToLineColumnPos(*m_user_expression_start_pos, m_transformed_text, + user_expr_line, user_expr_column); + else + return false; + + // The actual column where we have to complete is the start column of the + // user expression + the offset inside the user code that we were given. + const unsigned completion_column = user_expr_column + complete_pos; + parser.Complete(request, user_expr_line, completion_column, complete_pos); + + return true; +} + +bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx, + std::vector<lldb::addr_t> &args, + lldb::addr_t struct_address, + DiagnosticManager &diagnostic_manager) { + lldb::addr_t object_ptr = LLDB_INVALID_ADDRESS; + lldb::addr_t cmd_ptr = LLDB_INVALID_ADDRESS; + + if (m_needs_object_ptr) { + lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); + if (!frame_sp) + return true; + + ConstString object_name; + + if (m_in_cplusplus_method) { + object_name.SetCString("this"); + } else if (m_in_objectivec_method) { + object_name.SetCString("self"); + } else { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "need object pointer but don't know the language"); + return false; + } + + Status object_ptr_error; + + if (m_ctx_obj) { + AddressType address_type; + object_ptr = m_ctx_obj->GetAddressOf(false, &address_type); + if (object_ptr == LLDB_INVALID_ADDRESS || + address_type != eAddressTypeLoad) + object_ptr_error.SetErrorString("Can't get context object's " + "debuggee address"); + } else + object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error); + + if (!object_ptr_error.Success()) { + exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Printf( + "warning: `%s' is not accessible (substituting 0)\n", + object_name.AsCString()); + object_ptr = 0; + } + + if (m_in_objectivec_method) { + ConstString cmd_name("_cmd"); + + cmd_ptr = GetObjectPointer(frame_sp, cmd_name, object_ptr_error); + + if (!object_ptr_error.Success()) { + diagnostic_manager.Printf( + eDiagnosticSeverityWarning, + "couldn't get cmd pointer (substituting NULL): %s", + object_ptr_error.AsCString()); + cmd_ptr = 0; + } + } + + args.push_back(object_ptr); + + if (m_in_objectivec_method) + args.push_back(cmd_ptr); + + args.push_back(struct_address); + } else { + args.push_back(struct_address); + } + return true; +} + +lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization( + ExecutionContextScope *exe_scope) { + return m_result_delegate.GetVariable(); +} + +void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap( + ExecutionContext &exe_ctx, + Materializer::PersistentVariableDelegate &delegate, + bool keep_result_in_memory, + ValueObject *ctx_obj) { + m_expr_decl_map_up.reset(new ClangExpressionDeclMap( + keep_result_in_memory, &delegate, exe_ctx.GetTargetSP(), + exe_ctx.GetTargetRef().GetClangASTImporter(), ctx_obj)); +} + +clang::ASTConsumer * +ClangUserExpression::ClangUserExpressionHelper::ASTTransformer( + clang::ASTConsumer *passthrough) { + m_result_synthesizer_up.reset( + new ASTResultSynthesizer(passthrough, m_top_level, m_target)); + + return m_result_synthesizer_up.get(); +} + +void ClangUserExpression::ClangUserExpressionHelper::CommitPersistentDecls() { + if (m_result_synthesizer_up) { + m_result_synthesizer_up->CommitPersistentDecls(); + } +} + +ConstString ClangUserExpression::ResultDelegate::GetName() { + auto prefix = m_persistent_state->GetPersistentVariablePrefix(); + return m_persistent_state->GetNextPersistentVariableName(*m_target_sp, + prefix); +} + +void ClangUserExpression::ResultDelegate::DidDematerialize( + lldb::ExpressionVariableSP &variable) { + m_variable = variable; +} + +void ClangUserExpression::ResultDelegate::RegisterPersistentState( + PersistentExpressionState *persistent_state) { + m_persistent_state = persistent_state; +} + +lldb::ExpressionVariableSP &ClangUserExpression::ResultDelegate::GetVariable() { + return m_variable; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h new file mode 100644 index 00000000000..00cbffa7fd6 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h @@ -0,0 +1,254 @@ +//===-- ClangUserExpression.h -----------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangUserExpression_h_ +#define liblldb_ClangUserExpression_h_ + +#include <vector> + +#include "ASTResultSynthesizer.h" +#include "ASTStructExtractor.h" +#include "ClangExpressionDeclMap.h" +#include "ClangExpressionHelper.h" +#include "ClangExpressionSourceCode.h" +#include "ClangExpressionVariable.h" +#include "IRForTarget.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/LLVMUserExpression.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// \class ClangUserExpression ClangUserExpression.h +/// "lldb/Expression/ClangUserExpression.h" Encapsulates a single expression +/// for use with Clang +/// +/// LLDB uses expressions for various purposes, notably to call functions +/// and as a backend for the expr command. ClangUserExpression encapsulates +/// the objects needed to parse and interpret or JIT an expression. It uses +/// the Clang parser to produce LLVM IR from the expression. +class ClangUserExpression : public LLVMUserExpression { + // LLVM RTTI support + static char ID; + +public: + bool isA(const void *ClassID) const override { + return ClassID == &ID || LLVMUserExpression::isA(ClassID); + } + static bool classof(const Expression *obj) { return obj->isA(&ID); } + + enum { kDefaultTimeout = 500000u }; + + class ClangUserExpressionHelper : public ClangExpressionHelper { + public: + ClangUserExpressionHelper(Target &target, bool top_level) + : m_target(target), m_top_level(top_level) {} + + ~ClangUserExpressionHelper() override = default; + + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + ClangExpressionDeclMap *DeclMap() override { + return m_expr_decl_map_up.get(); + } + + void ResetDeclMap() { m_expr_decl_map_up.reset(); } + + void ResetDeclMap(ExecutionContext &exe_ctx, + Materializer::PersistentVariableDelegate &result_delegate, + bool keep_result_in_memory, + ValueObject *ctx_obj); + + /// Return the object that the parser should allow to access ASTs. May be + /// NULL if the ASTs do not need to be transformed. + /// + /// \param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + clang::ASTConsumer * + ASTTransformer(clang::ASTConsumer *passthrough) override; + + void CommitPersistentDecls() override; + + private: + Target &m_target; + std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up; + std::unique_ptr<ASTStructExtractor> m_struct_extractor_up; ///< The class + ///that generates + ///the argument + ///struct layout. + std::unique_ptr<ASTResultSynthesizer> m_result_synthesizer_up; + bool m_top_level; + }; + + /// Constructor + /// + /// \param[in] expr + /// The expression to parse. + /// + /// \param[in] prefix + /// If non-NULL, a C string containing translation-unit level + /// definitions to be included when the expression is parsed. + /// + /// \param[in] language + /// If not eLanguageTypeUnknown, a language to use when parsing + /// the expression. Currently restricted to those languages + /// supported by Clang. + /// + /// \param[in] desired_type + /// If not eResultTypeAny, the type to use for the expression + /// result. + /// + /// \param[in] options + /// Additional options for the expression. + /// + /// \param[in] ctx_obj + /// The object (if any) in which context the expression + /// must be evaluated. For details see the comment to + /// `UserExpression::Evaluate`. + ClangUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, lldb::LanguageType language, + ResultType desired_type, + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj); + + ~ClangUserExpression() override; + + /// Parse the expression + /// + /// \param[in] diagnostic_manager + /// A diagnostic manager to report parse errors and warnings to. + /// + /// \param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of functions, types of + /// variables, persistent variables, etc.) + /// + /// \param[in] execution_policy + /// Determines whether interpretation is possible or mandatory. + /// + /// \param[in] keep_result_in_memory + /// True if the resulting persistent variable should reside in + /// target memory, if applicable. + /// + /// \return + /// True on success (no errors); false otherwise. + bool Parse(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory, bool generate_debug_info) override; + + bool Complete(ExecutionContext &exe_ctx, CompletionRequest &request, + unsigned complete_pos) override; + + ExpressionTypeSystemHelper *GetTypeSystemHelper() override { + return &m_type_system_helper; + } + + ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); } + + void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); } + + void ResetDeclMap(ExecutionContext &exe_ctx, + Materializer::PersistentVariableDelegate &result_delegate, + bool keep_result_in_memory) { + m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate, + keep_result_in_memory, + m_ctx_obj); + } + + lldb::ExpressionVariableSP + GetResultAfterDematerialization(ExecutionContextScope *exe_scope) override; + + bool DidImportCxxModules() const { return m_imported_cpp_modules; } + +private: + /// Populate m_in_cplusplus_method and m_in_objectivec_method based on the + /// environment. + + void ScanContext(ExecutionContext &exe_ctx, + lldb_private::Status &err) override; + + bool AddArguments(ExecutionContext &exe_ctx, std::vector<lldb::addr_t> &args, + lldb::addr_t struct_address, + DiagnosticManager &diagnostic_manager) override; + + void CreateSourceCode(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + std::vector<std::string> modules_to_import, + bool for_completion); + void UpdateLanguageForExpr(); + bool SetupPersistentState(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx); + bool PrepareForParsing(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, bool for_completion); + + ClangUserExpressionHelper m_type_system_helper; + + class ResultDelegate : public Materializer::PersistentVariableDelegate { + public: + ResultDelegate(lldb::TargetSP target) : m_target_sp(target) {} + ConstString GetName() override; + void DidDematerialize(lldb::ExpressionVariableSP &variable) override; + + void RegisterPersistentState(PersistentExpressionState *persistent_state); + lldb::ExpressionVariableSP &GetVariable(); + + private: + PersistentExpressionState *m_persistent_state; + lldb::ExpressionVariableSP m_variable; + lldb::TargetSP m_target_sp; + }; + + /// The language type of the current expression. + lldb::LanguageType m_expr_lang = lldb::eLanguageTypeUnknown; + /// The include directories that should be used when parsing the expression. + std::vector<std::string> m_include_directories; + + /// The absolute character position in the transformed source code where the + /// user code (as typed by the user) starts. If the variable is empty, then we + /// were not able to calculate this position. + llvm::Optional<size_t> m_user_expression_start_pos; + ResultDelegate m_result_delegate; + ClangPersistentVariables *m_clang_state; + std::unique_ptr<ClangExpressionSourceCode> m_source_code; + /// File name used for the expression. + std::string m_filename; + + /// The object (if any) in which context the expression is evaluated. + /// See the comment to `UserExpression::Evaluate` for details. + ValueObject *m_ctx_obj; + + /// True iff this expression explicitly imported C++ modules. + bool m_imported_cpp_modules = false; + + /// True if the expression parser should enforce the presence of a valid class + /// pointer in order to generate the expression as a method. + bool m_enforce_valid_object = true; + /// True if the expression is compiled as a C++ member function (true if it + /// was parsed when exe_ctx was in a C++ method). + bool m_in_cplusplus_method = false; + /// True if the expression is compiled as an Objective-C method (true if it + /// was parsed when exe_ctx was in an Objective-C method). + bool m_in_objectivec_method = false; + /// True if the expression is compiled as a static (or class) method + /// (currently true if it was parsed when exe_ctx was in an Objective-C class + /// method). + bool m_in_static_method = false; + /// True if "this" or "self" must be looked up and passed in. False if the + /// expression doesn't really use them and they can be NULL. + bool m_needs_object_ptr = false; +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUserExpression_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp new file mode 100644 index 00000000000..199e4898e11 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -0,0 +1,165 @@ +//===-- ClangUtilityFunction.cpp ---------------------------------*- C++-*-===// +// +// 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 "lldb/Host/Config.h" + +#include "ClangUtilityFunction.h" +#include "ClangExpressionDeclMap.h" +#include "ClangExpressionParser.h" +#include "ClangExpressionSourceCode.h" + +#include <stdio.h> +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + + +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; + +char ClangUtilityFunction::ID; + +/// Constructor +/// +/// \param[in] text +/// The text of the function. Must be a full translation unit. +/// +/// \param[in] name +/// The name of the function, as used in the text. +ClangUtilityFunction::ClangUtilityFunction(ExecutionContextScope &exe_scope, + const char *text, const char *name) + : UtilityFunction(exe_scope, text, name) { + m_function_text.assign(ClangExpressionSourceCode::g_expression_prefix); + if (text && text[0]) + m_function_text.append(text); +} + +ClangUtilityFunction::~ClangUtilityFunction() {} + +/// Install the utility function into a process +/// +/// \param[in] diagnostic_manager +/// A diagnostic manager to report errors and warnings to. +/// +/// \param[in] exe_ctx +/// The execution context to install the utility function to. +/// +/// \return +/// True on success (no errors); false otherwise. +bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx) { + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) { + diagnostic_manager.PutString(eDiagnosticSeverityWarning, + "already installed"); + return false; + } + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) { + diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) { + diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid process"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + bool keep_result_in_memory = false; + + ResetDeclMap(exe_ctx, keep_result_in_memory); + + if (!DeclMap()->WillParse(exe_ctx, nullptr)) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "current process state is unsuitable for expression parsing"); + return false; + } + + const bool generate_debug_info = true; + ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this, + generate_debug_info); + + unsigned num_errors = parser.Parse(diagnostic_manager); + + if (num_errors) { + ResetDeclMap(); + + return false; + } + + ////////////////////////////////// + // JIT the output of the parser + // + + bool can_interpret = false; // should stay that way + + Status jit_error = parser.PrepareForExecution( + m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx, + can_interpret, eExecutionPolicyAlways); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) { + m_jit_process_wp = process->shared_from_this(); + if (parser.GetGenerateDebugInfo()) { + lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule()); + + if (jit_module_sp) { + ConstString const_func_name(FunctionName()); + FileSpec jit_file; + jit_file.GetFilename() = const_func_name; + jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString()); + m_jit_module_wp = jit_module_sp; + target->GetImages().Append(jit_module_sp); + } + } + } + + DeclMap()->DidParse(); + + ResetDeclMap(); + + if (jit_error.Success()) { + return true; + } else { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) { + diagnostic_manager.Printf(eDiagnosticSeverityError, "%s", error_cstr); + } else { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "expression can't be interpreted or run"); + } + return false; + } +} + +void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap( + ExecutionContext &exe_ctx, bool keep_result_in_memory) { + m_expr_decl_map_up.reset(new ClangExpressionDeclMap( + keep_result_in_memory, nullptr, exe_ctx.GetTargetSP(), + exe_ctx.GetTargetRef().GetClangASTImporter(), nullptr)); +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h new file mode 100644 index 00000000000..9efaa0254c3 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h @@ -0,0 +1,110 @@ +//===-- ClangUtilityFunction.h ----------------------------------*- C++ -*-===// +// +// 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 liblldb_ClangUtilityFunction_h_ +#define liblldb_ClangUtilityFunction_h_ + +#include <map> +#include <string> +#include <vector> + +#include "ClangExpressionHelper.h" + +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// \class ClangUtilityFunction ClangUtilityFunction.h +/// "lldb/Expression/ClangUtilityFunction.h" Encapsulates a single expression +/// for use with Clang +/// +/// LLDB uses expressions for various purposes, notably to call functions +/// and as a backend for the expr command. ClangUtilityFunction encapsulates +/// a self-contained function meant to be used from other code. Utility +/// functions can perform error-checking for ClangUserExpressions, or can +/// simply provide a way to push a function into the target for the debugger +/// to call later on. +class ClangUtilityFunction : public UtilityFunction { + // LLVM RTTI support + static char ID; + +public: + bool isA(const void *ClassID) const override { + return ClassID == &ID || UtilityFunction::isA(ClassID); + } + static bool classof(const Expression *obj) { return obj->isA(&ID); } + + class ClangUtilityFunctionHelper : public ClangExpressionHelper { + public: + ClangUtilityFunctionHelper() {} + + ~ClangUtilityFunctionHelper() override {} + + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + ClangExpressionDeclMap *DeclMap() override { + return m_expr_decl_map_up.get(); + } + + void ResetDeclMap() { m_expr_decl_map_up.reset(); } + + void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory); + + /// Return the object that the parser should allow to access ASTs. May be + /// NULL if the ASTs do not need to be transformed. + /// + /// \param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + clang::ASTConsumer * + ASTTransformer(clang::ASTConsumer *passthrough) override { + return nullptr; + } + + private: + std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up; + }; + /// Constructor + /// + /// \param[in] text + /// The text of the function. Must be a full translation unit. + /// + /// \param[in] name + /// The name of the function, as used in the text. + ClangUtilityFunction(ExecutionContextScope &exe_scope, const char *text, + const char *name); + + ~ClangUtilityFunction() override; + + ExpressionTypeSystemHelper *GetTypeSystemHelper() override { + return &m_type_system_helper; + } + + ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); } + + void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); } + + void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory) { + m_type_system_helper.ResetDeclMap(exe_ctx, keep_result_in_memory); + } + + bool Install(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx) override; + +private: + ClangUtilityFunctionHelper m_type_system_helper; ///< The map to use when + ///parsing and materializing + ///the expression. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUtilityFunction_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp new file mode 100644 index 00000000000..51ae73285b5 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp @@ -0,0 +1,82 @@ +//===-- CppModuleConfiguration.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 "CppModuleConfiguration.h" + +#include "ClangHost.h" +#include "lldb/Host/FileSystem.h" + +using namespace lldb_private; + +bool CppModuleConfiguration::SetOncePath::TrySet(llvm::StringRef path) { + // Setting for the first time always works. + if (m_first) { + m_path = path.str(); + m_valid = true; + m_first = false; + return true; + } + // Changing the path to the same value is fine. + if (m_path == path) + return true; + + // Changing the path after it was already set is not allowed. + m_valid = false; + return false; +} + +bool CppModuleConfiguration::analyzeFile(const FileSpec &f) { + using namespace llvm::sys::path; + // Convert to slashes to make following operations simpler. + std::string dir_buffer = convert_to_slash(f.GetDirectory().GetStringRef()); + llvm::StringRef posix_dir(dir_buffer); + + // Check for /c++/vX/ that is used by libc++. + static llvm::Regex libcpp_regex(R"regex(/c[+][+]/v[0-9]/)regex"); + if (libcpp_regex.match(f.GetPath())) { + // Strip away libc++'s /experimental directory if there is one. + posix_dir.consume_back("/experimental"); + return m_std_inc.TrySet(posix_dir); + } + + // Check for /usr/include. On Linux this might be /usr/include/bits, so + // we should remove that '/bits' suffix to get the actual include directory. + if (posix_dir.endswith("/usr/include/bits")) + posix_dir.consume_back("/bits"); + if (posix_dir.endswith("/usr/include")) + return m_c_inc.TrySet(posix_dir); + + // File wasn't interesting, continue analyzing. + return true; +} + +bool CppModuleConfiguration::hasValidConfig() { + // We all these include directories to have a valid usable configuration. + return m_c_inc.Valid() && m_std_inc.Valid(); +} + +CppModuleConfiguration::CppModuleConfiguration( + const FileSpecList &support_files) { + // Analyze all files we were given to build the configuration. + bool error = !llvm::all_of(support_files, + std::bind(&CppModuleConfiguration::analyzeFile, + this, std::placeholders::_1)); + // If we have a valid configuration at this point, set the + // include directories and module list that should be used. + if (!error && hasValidConfig()) { + // Calculate the resource directory for LLDB. + llvm::SmallString<256> resource_dir; + llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(), + "include"); + m_resource_inc = resource_dir.str(); + + // This order matches the way Clang orders these directories. + m_include_dirs = {m_std_inc.Get(), m_resource_inc, m_c_inc.Get()}; + m_imported_modules = {"std"}; + } +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h new file mode 100644 index 00000000000..8e892e37d0d --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h @@ -0,0 +1,84 @@ +//===-- CppModuleConfiguration.h --------------------------------*- C++ -*-===// +// +// 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 liblldb_CppModuleConfiguration_h_ +#define liblldb_CppModuleConfiguration_h_ + +#include <lldb/Core/FileSpecList.h> +#include <llvm/Support/Regex.h> + +namespace lldb_private { + +/// A Clang configuration when importing C++ modules. +/// +/// Includes a list of include paths that should be used when importing +/// and a list of modules that can be imported. Currently only used when +/// importing the 'std' module and its dependencies. +class CppModuleConfiguration { + /// Utility class for a path that can only be set once. + class SetOncePath { + std::string m_path; + bool m_valid = false; + /// True iff this path hasn't been set yet. + bool m_first = true; + + public: + /// Try setting the path. Returns true if the path was set and false if + /// the path was already set. + LLVM_NODISCARD bool TrySet(llvm::StringRef path); + /// Return the path if there is one. + std::string Get() const { + assert(m_valid && "Called Get() on an invalid SetOncePath?"); + return m_path; + } + /// Returns true iff this path was set exactly once so far. + bool Valid() const { return m_valid; } + }; + + /// If valid, the include path used for the std module. + SetOncePath m_std_inc; + /// If valid, the include path to the C library (e.g. /usr/include). + SetOncePath m_c_inc; + /// The Clang resource include path for this configuration. + std::string m_resource_inc; + + std::vector<std::string> m_include_dirs; + std::vector<std::string> m_imported_modules; + + /// Analyze a given source file to build the current configuration. + /// Returns false iff there was a fatal error that makes analyzing any + /// further files pointless as the configuration is now invalid. + bool analyzeFile(const FileSpec &f); + +public: + /// Creates a configuraiton by analyzing the given list of used source files. + /// + /// Currently only looks at the used paths and doesn't actually access the + /// files on the disk. + explicit CppModuleConfiguration(const FileSpecList &support_files); + /// Creates an empty and invalid configuration. + CppModuleConfiguration() {} + + /// Returns true iff this is a valid configuration that can be used to + /// load and compile modules. + bool hasValidConfig(); + + /// Returns a list of include directories that should be used when using this + /// configuration (e.g. {"/usr/include", "/usr/include/c++/v1"}). + llvm::ArrayRef<std::string> GetIncludeDirs() const { return m_include_dirs; } + + /// Returns a list of (top level) modules that should be imported when using + /// this configuration (e.g. {"std"}). + llvm::ArrayRef<std::string> GetImportedModules() const { + return m_imported_modules; + } +}; + +} // namespace lldb_private + +#endif diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp new file mode 100644 index 00000000000..d5ffb9529f3 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp @@ -0,0 +1,591 @@ +//===-- IRDynamicChecks.cpp -------------------------------------*- C++ -*-===// +// +// 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 "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/raw_ostream.h" + +#include "IRDynamicChecks.h" + +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +using namespace llvm; +using namespace lldb_private; + +static char ID; + +#define VALID_POINTER_CHECK_NAME "_$__lldb_valid_pointer_check" +#define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check" + +static const char g_valid_pointer_check_text[] = + "extern \"C\" void\n" + "_$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n" + "{\n" + " unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n" + "}"; + +ClangDynamicCheckerFunctions::ClangDynamicCheckerFunctions() + : DynamicCheckerFunctions(DCF_Clang) {} + +ClangDynamicCheckerFunctions::~ClangDynamicCheckerFunctions() = default; + +bool ClangDynamicCheckerFunctions::Install( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) { + Status error; + m_valid_pointer_check.reset( + exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage( + g_valid_pointer_check_text, lldb::eLanguageTypeC, + VALID_POINTER_CHECK_NAME, error)); + if (error.Fail()) + return false; + + if (!m_valid_pointer_check->Install(diagnostic_manager, exe_ctx)) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process) { + ObjCLanguageRuntime *objc_language_runtime = + ObjCLanguageRuntime::Get(*process); + + if (objc_language_runtime) { + m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker( + VALID_OBJC_OBJECT_CHECK_NAME)); + + if (!m_objc_object_check->Install(diagnostic_manager, exe_ctx)) + return false; + } + } + + return true; +} + +bool ClangDynamicCheckerFunctions::DoCheckersExplainStop(lldb::addr_t addr, + Stream &message) { + // FIXME: We have to get the checkers to know why they scotched the call in + // more detail, + // so we can print a better message here. + if (m_valid_pointer_check && m_valid_pointer_check->ContainsAddress(addr)) { + message.Printf("Attempted to dereference an invalid pointer."); + return true; + } else if (m_objc_object_check && + m_objc_object_check->ContainsAddress(addr)) { + message.Printf("Attempted to dereference an invalid ObjC Object or send it " + "an unrecognized selector"); + return true; + } + return false; +} + +static std::string PrintValue(llvm::Value *V, bool truncate = false) { + std::string s; + raw_string_ostream rso(s); + V->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +/// \class Instrumenter IRDynamicChecks.cpp +/// Finds and instruments individual LLVM IR instructions +/// +/// When instrumenting LLVM IR, it is frequently desirable to first search for +/// instructions, and then later modify them. This way iterators remain +/// intact, and multiple passes can look at the same code base without +/// treading on each other's toes. +/// +/// The Instrumenter class implements this functionality. A client first +/// calls Inspect on a function, which populates a list of instructions to be +/// instrumented. Then, later, when all passes' Inspect functions have been +/// called, the client calls Instrument, which adds the desired +/// instrumentation. +/// +/// A subclass of Instrumenter must override InstrumentInstruction, which +/// is responsible for adding whatever instrumentation is necessary. +/// +/// A subclass of Instrumenter may override: +/// +/// - InspectInstruction [default: does nothing] +/// +/// - InspectBasicBlock [default: iterates through the instructions in a +/// basic block calling InspectInstruction] +/// +/// - InspectFunction [default: iterates through the basic blocks in a +/// function calling InspectBasicBlock] +class Instrumenter { +public: + /// Constructor + /// + /// \param[in] module + /// The module being instrumented. + Instrumenter(llvm::Module &module, + std::shared_ptr<UtilityFunction> checker_function) + : m_module(module), m_checker_function(checker_function), + m_i8ptr_ty(nullptr), m_intptr_ty(nullptr) {} + + virtual ~Instrumenter() = default; + + /// Inspect a function to find instructions to instrument + /// + /// \param[in] function + /// The function to inspect. + /// + /// \return + /// True on success; false on error. + bool Inspect(llvm::Function &function) { return InspectFunction(function); } + + /// Instrument all the instructions found by Inspect() + /// + /// \return + /// True on success; false on error. + bool Instrument() { + for (InstIterator ii = m_to_instrument.begin(), + last_ii = m_to_instrument.end(); + ii != last_ii; ++ii) { + if (!InstrumentInstruction(*ii)) + return false; + } + + return true; + } + +protected: + /// Add instrumentation to a single instruction + /// + /// \param[in] inst + /// The instruction to be instrumented. + /// + /// \return + /// True on success; false otherwise. + virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0; + + /// Register a single instruction to be instrumented + /// + /// \param[in] inst + /// The instruction to be instrumented. + void RegisterInstruction(llvm::Instruction &i) { + m_to_instrument.push_back(&i); + } + + /// Determine whether a single instruction is interesting to instrument, + /// and, if so, call RegisterInstruction + /// + /// \param[in] i + /// The instruction to be inspected. + /// + /// \return + /// False if there was an error scanning; true otherwise. + virtual bool InspectInstruction(llvm::Instruction &i) { return true; } + + /// Scan a basic block to see if any instructions are interesting + /// + /// \param[in] bb + /// The basic block to be inspected. + /// + /// \return + /// False if there was an error scanning; true otherwise. + virtual bool InspectBasicBlock(llvm::BasicBlock &bb) { + for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end(); + ii != last_ii; ++ii) { + if (!InspectInstruction(*ii)) + return false; + } + + return true; + } + + /// Scan a function to see if any instructions are interesting + /// + /// \param[in] f + /// The function to be inspected. + /// + /// \return + /// False if there was an error scanning; true otherwise. + virtual bool InspectFunction(llvm::Function &f) { + for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); + bbi != last_bbi; ++bbi) { + if (!InspectBasicBlock(*bbi)) + return false; + } + + return true; + } + + /// Build a function pointer for a function with signature void + /// (*)(uint8_t*) with a given address + /// + /// \param[in] start_address + /// The address of the function. + /// + /// \return + /// The function pointer, for use in a CallInst. + llvm::FunctionCallee BuildPointerValidatorFunc(lldb::addr_t start_address) { + llvm::Type *param_array[1]; + + param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy()); + + ArrayRef<llvm::Type *> params(param_array, 1); + + FunctionType *fun_ty = FunctionType::get( + llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = + ConstantInt::get(GetIntptrTy(), start_address, false); + return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)}; + } + + /// Build a function pointer for a function with signature void + /// (*)(uint8_t*, uint8_t*) with a given address + /// + /// \param[in] start_address + /// The address of the function. + /// + /// \return + /// The function pointer, for use in a CallInst. + llvm::FunctionCallee BuildObjectCheckerFunc(lldb::addr_t start_address) { + llvm::Type *param_array[2]; + + param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy()); + param_array[1] = const_cast<llvm::PointerType *>(GetI8PtrTy()); + + ArrayRef<llvm::Type *> params(param_array, 2); + + FunctionType *fun_ty = FunctionType::get( + llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = + ConstantInt::get(GetIntptrTy(), start_address, false); + return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)}; + } + + PointerType *GetI8PtrTy() { + if (!m_i8ptr_ty) + m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext()); + + return m_i8ptr_ty; + } + + IntegerType *GetIntptrTy() { + if (!m_intptr_ty) { + llvm::DataLayout data_layout(&m_module); + + m_intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + data_layout.getPointerSizeInBits()); + } + + return m_intptr_ty; + } + + typedef std::vector<llvm::Instruction *> InstVector; + typedef InstVector::iterator InstIterator; + + InstVector m_to_instrument; ///< List of instructions the inspector found + llvm::Module &m_module; ///< The module which is being instrumented + std::shared_ptr<UtilityFunction> + m_checker_function; ///< The dynamic checker function for the process + +private: + PointerType *m_i8ptr_ty; + IntegerType *m_intptr_ty; +}; + +class ValidPointerChecker : public Instrumenter { +public: + ValidPointerChecker(llvm::Module &module, + std::shared_ptr<UtilityFunction> checker_function) + : Instrumenter(module, checker_function), + m_valid_pointer_check_func(nullptr) {} + + ~ValidPointerChecker() override = default; + +protected: + bool InstrumentInstruction(llvm::Instruction *inst) override { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOGF(log, "Instrumenting load/store instruction: %s\n", + PrintValue(inst).c_str()); + + if (!m_valid_pointer_check_func) + m_valid_pointer_check_func = + BuildPointerValidatorFunc(m_checker_function->StartAddress()); + + llvm::Value *dereferenced_ptr = nullptr; + + if (llvm::LoadInst *li = dyn_cast<llvm::LoadInst>(inst)) + dereferenced_ptr = li->getPointerOperand(); + else if (llvm::StoreInst *si = dyn_cast<llvm::StoreInst>(inst)) + dereferenced_ptr = si->getPointerOperand(); + else + return false; + + // Insert an instruction to cast the loaded value to int8_t* + + BitCastInst *bit_cast = + new BitCastInst(dereferenced_ptr, GetI8PtrTy(), "", inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[1]; + + arg_array[0] = bit_cast; + + llvm::ArrayRef<llvm::Value *> args(arg_array, 1); + + CallInst::Create(m_valid_pointer_check_func, args, "", inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) override { + if (dyn_cast<llvm::LoadInst>(&i) || dyn_cast<llvm::StoreInst>(&i)) + RegisterInstruction(i); + + return true; + } + +private: + llvm::FunctionCallee m_valid_pointer_check_func; +}; + +class ObjcObjectChecker : public Instrumenter { +public: + ObjcObjectChecker(llvm::Module &module, + std::shared_ptr<UtilityFunction> checker_function) + : Instrumenter(module, checker_function), + m_objc_object_check_func(nullptr) {} + + ~ObjcObjectChecker() override = default; + + enum msgSend_type { + eMsgSend = 0, + eMsgSendSuper, + eMsgSendSuper_stret, + eMsgSend_fpret, + eMsgSend_stret + }; + + std::map<llvm::Instruction *, msgSend_type> msgSend_types; + +protected: + bool InstrumentInstruction(llvm::Instruction *inst) override { + CallInst *call_inst = dyn_cast<CallInst>(inst); + + if (!call_inst) + return false; // call_inst really shouldn't be nullptr, because otherwise + // InspectInstruction wouldn't have registered it + + if (!m_objc_object_check_func) + m_objc_object_check_func = + BuildObjectCheckerFunc(m_checker_function->StartAddress()); + + // id objc_msgSend(id theReceiver, SEL theSelector, ...) + + llvm::Value *target_object; + llvm::Value *selector; + + switch (msgSend_types[inst]) { + case eMsgSend: + case eMsgSend_fpret: + // On arm64, clang uses objc_msgSend for scalar and struct return + // calls. The call instruction will record which was used. + if (call_inst->hasStructRetAttr()) { + target_object = call_inst->getArgOperand(1); + selector = call_inst->getArgOperand(2); + } else { + target_object = call_inst->getArgOperand(0); + selector = call_inst->getArgOperand(1); + } + break; + case eMsgSend_stret: + target_object = call_inst->getArgOperand(1); + selector = call_inst->getArgOperand(2); + break; + case eMsgSendSuper: + case eMsgSendSuper_stret: + return true; + } + + // These objects should always be valid according to Sean Calannan + assert(target_object); + assert(selector); + + // Insert an instruction to cast the receiver id to int8_t* + + BitCastInst *bit_cast = + new BitCastInst(target_object, GetI8PtrTy(), "", inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[2]; + + arg_array[0] = bit_cast; + arg_array[1] = selector; + + ArrayRef<llvm::Value *> args(arg_array, 2); + + CallInst::Create(m_objc_object_check_func, args, "", inst); + + return true; + } + + static llvm::Function *GetFunction(llvm::Value *value) { + if (llvm::Function *function = llvm::dyn_cast<llvm::Function>(value)) { + return function; + } + + if (llvm::ConstantExpr *const_expr = + llvm::dyn_cast<llvm::ConstantExpr>(value)) { + switch (const_expr->getOpcode()) { + default: + return nullptr; + case llvm::Instruction::BitCast: + return GetFunction(const_expr->getOperand(0)); + } + } + + return nullptr; + } + + static llvm::Function *GetCalledFunction(llvm::CallInst *inst) { + return GetFunction(inst->getCalledValue()); + } + + bool InspectInstruction(llvm::Instruction &i) override { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + CallInst *call_inst = dyn_cast<CallInst>(&i); + + if (call_inst) { + const llvm::Function *called_function = GetCalledFunction(call_inst); + + if (!called_function) + return true; + + std::string name_str = called_function->getName().str(); + const char *name_cstr = name_str.c_str(); + + LLDB_LOGF(log, "Found call to %s: %s\n", name_cstr, + PrintValue(call_inst).c_str()); + + if (name_str.find("objc_msgSend") == std::string::npos) + return true; + + if (!strcmp(name_cstr, "objc_msgSend")) { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_stret")) { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_stret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_fpret")) { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_fpret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper")) { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper_stret; + return true; + } + + LLDB_LOGF(log, + "Function name '%s' contains 'objc_msgSend' but is not handled", + name_str.c_str()); + + return true; + } + + return true; + } + +private: + llvm::FunctionCallee m_objc_object_check_func; +}; + +IRDynamicChecks::IRDynamicChecks( + ClangDynamicCheckerFunctions &checker_functions, const char *func_name) + : ModulePass(ID), m_func_name(func_name), + m_checker_functions(checker_functions) {} + +IRDynamicChecks::~IRDynamicChecks() = default; + +bool IRDynamicChecks::runOnModule(llvm::Module &M) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + llvm::Function *function = M.getFunction(StringRef(m_func_name)); + + if (!function) { + LLDB_LOGF(log, "Couldn't find %s() in the module", m_func_name.c_str()); + + return false; + } + + if (m_checker_functions.m_valid_pointer_check) { + ValidPointerChecker vpc(M, m_checker_functions.m_valid_pointer_check); + + if (!vpc.Inspect(*function)) + return false; + + if (!vpc.Instrument()) + return false; + } + + if (m_checker_functions.m_objc_object_check) { + ObjcObjectChecker ooc(M, m_checker_functions.m_objc_object_check); + + if (!ooc.Inspect(*function)) + return false; + + if (!ooc.Instrument()) + return false; + } + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream oss(s); + + M.print(oss, nullptr); + + oss.flush(); + + LLDB_LOGF(log, "Module after dynamic checks: \n%s", s.c_str()); + } + + return true; +} + +void IRDynamicChecks::assignPassManager(PMStack &PMS, PassManagerType T) {} + +PassManagerType IRDynamicChecks::getPotentialPassManagerType() const { + return PMT_ModulePassManager; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h new file mode 100644 index 00000000000..5b9c8007ab7 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h @@ -0,0 +1,127 @@ +//===-- IRDynamicChecks.h ---------------------------------------*- C++ -*-===// +// +// 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 liblldb_IRDynamicChecks_h_ +#define liblldb_IRDynamicChecks_h_ + +#include "lldb/Expression/DynamicCheckerFunctions.h" +#include "lldb/lldb-types.h" +#include "llvm/Pass.h" + +namespace llvm { +class BasicBlock; +class Module; +} + +namespace lldb_private { + +class ExecutionContext; +class Stream; + +class ClangDynamicCheckerFunctions + : public lldb_private::DynamicCheckerFunctions { +public: + /// Constructor + ClangDynamicCheckerFunctions(); + + /// Destructor + virtual ~ClangDynamicCheckerFunctions(); + + static bool classof(const DynamicCheckerFunctions *checker_funcs) { + return checker_funcs->GetKind() == DCF_Clang; + } + + /// Install the utility functions into a process. This binds the instance + /// of DynamicCheckerFunctions to that process. + /// + /// \param[in] diagnostic_manager + /// A diagnostic manager to report errors to. + /// + /// \param[in] exe_ctx + /// The execution context to install the functions into. + /// + /// \return + /// True on success; false on failure, or if the functions have + /// already been installed. + bool Install(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx) override; + + bool DoCheckersExplainStop(lldb::addr_t addr, Stream &message) override; + + std::shared_ptr<UtilityFunction> m_valid_pointer_check; + std::shared_ptr<UtilityFunction> m_objc_object_check; +}; + +/// \class IRDynamicChecks IRDynamicChecks.h +/// "lldb/Expression/IRDynamicChecks.h" Adds dynamic checks to a user-entered +/// expression to reduce its likelihood of crashing +/// +/// When an IR function is executed in the target process, it may cause +/// crashes or hangs by dereferencing NULL pointers, trying to call +/// Objective-C methods on objects that do not respond to them, and so forth. +/// +/// IRDynamicChecks adds calls to the functions in DynamicCheckerFunctions to +/// appropriate locations in an expression's IR. +class IRDynamicChecks : public llvm::ModulePass { +public: + /// Constructor + /// + /// \param[in] checker_functions + /// The checker functions for the target process. + /// + /// \param[in] func_name + /// The name of the function to prepare for execution in the target. + IRDynamicChecks(ClangDynamicCheckerFunctions &checker_functions, + const char *func_name = "$__lldb_expr"); + + /// Destructor + ~IRDynamicChecks() override; + + /// Run this IR transformer on a single module + /// + /// \param[in] M + /// The module to run on. This module is searched for the function + /// $__lldb_expr, and that function is passed to the passes one by + /// one. + /// + /// \return + /// True on success; false otherwise + bool runOnModule(llvm::Module &M) override; + + /// Interface stub + void assignPassManager( + llvm::PMStack &PMS, + llvm::PassManagerType T = llvm::PMT_ModulePassManager) override; + + /// Returns PMT_ModulePassManager + llvm::PassManagerType getPotentialPassManagerType() const override; + +private: + /// A basic block-level pass to find all pointer dereferences and + /// validate them before use. + + /// The top-level pass implementation + /// + /// \param[in] M + /// The module currently being processed. + /// + /// \param[in] BB + /// The basic block currently being processed. + /// + /// \return + /// True on success; false otherwise + bool FindDataLoads(llvm::Module &M, llvm::BasicBlock &BB); + + std::string m_func_name; ///< The name of the function to add checks to + ClangDynamicCheckerFunctions + &m_checker_functions; ///< The checker functions for the process +}; + +} // namespace lldb_private + +#endif // liblldb_IRDynamicChecks_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp new file mode 100644 index 00000000000..103a7ee46f3 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp @@ -0,0 +1,2033 @@ +//===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===// +// +// 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 "IRForTarget.h" + +#include "ClangExpressionDeclMap.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ValueSymbolTable.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO.h" + +#include "clang/AST/ASTContext.h" + +#include "lldb/Core/dwarf.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" + +#include <map> + +using namespace llvm; + +static char ID; + +typedef SmallVector<Instruction *, 2> InstrList; + +IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker) + : m_maker(maker), m_values() {} + +IRForTarget::FunctionValueCache::~FunctionValueCache() {} + +llvm::Value * +IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) { + if (!m_values.count(function)) { + llvm::Value *ret = m_maker(function); + m_values[function] = ret; + return ret; + } + return m_values[function]; +} + +static llvm::Value *FindEntryInstruction(llvm::Function *function) { + if (function->empty()) + return nullptr; + + return function->getEntryBlock().getFirstNonPHIOrDbg(); +} + +IRForTarget::IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, + bool resolve_vars, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Stream &error_stream, + const char *func_name) + : ModulePass(ID), m_resolve_vars(resolve_vars), m_func_name(func_name), + m_module(nullptr), m_decl_map(decl_map), + m_CFStringCreateWithBytes(nullptr), m_sel_registerName(nullptr), + m_objc_getClass(nullptr), m_intptr_ty(nullptr), + m_error_stream(error_stream), m_execution_unit(execution_unit), + m_result_store(nullptr), m_result_is_pointer(false), + m_reloc_placeholder(nullptr), + m_entry_instruction_finder(FindEntryInstruction) {} + +/* Handy utility functions used at several places in the code */ + +static std::string PrintValue(const Value *value, bool truncate = false) { + std::string s; + if (value) { + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + } + return s; +} + +static std::string PrintType(const llvm::Type *type, bool truncate = false) { + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +IRForTarget::~IRForTarget() {} + +bool IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) { + llvm_function.setLinkage(GlobalValue::ExternalLinkage); + + return true; +} + +clang::NamedDecl *IRForTarget::DeclForGlobal(const GlobalValue *global_val, + Module *module) { + NamedMDNode *named_metadata = + module->getNamedMetadata("clang.global.decl.ptrs"); + + if (!named_metadata) + return nullptr; + + unsigned num_nodes = named_metadata->getNumOperands(); + unsigned node_index; + + for (node_index = 0; node_index < num_nodes; ++node_index) { + llvm::MDNode *metadata_node = + dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index)); + if (!metadata_node) + return nullptr; + + if (metadata_node->getNumOperands() != 2) + continue; + + if (mdconst::dyn_extract_or_null<GlobalValue>( + metadata_node->getOperand(0)) != global_val) + continue; + + ConstantInt *constant_int = + mdconst::dyn_extract<ConstantInt>(metadata_node->getOperand(1)); + + if (!constant_int) + return nullptr; + + uintptr_t ptr = constant_int->getZExtValue(); + + return reinterpret_cast<clang::NamedDecl *>(ptr); + } + + return nullptr; +} + +clang::NamedDecl *IRForTarget::DeclForGlobal(GlobalValue *global_val) { + return DeclForGlobal(global_val, m_module); +} + +/// Returns true iff the mangled symbol is for a static guard variable. +static bool isGuardVariableSymbol(llvm::StringRef mangled_symbol, + bool check_ms_abi = true) { + bool result = mangled_symbol.startswith("_ZGV"); // Itanium ABI guard variable + if (check_ms_abi) + result |= mangled_symbol.endswith("@4IA"); // Microsoft ABI + return result; +} + +bool IRForTarget::CreateResultVariable(llvm::Function &llvm_function) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_resolve_vars) + return true; + + // Find the result variable. If it doesn't exist, we can give up right here. + + ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable(); + + llvm::StringRef result_name; + bool found_result = false; + + for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) { + result_name = value_symbol.first(); + + // Check if this is a guard variable. It seems this causes some hiccups + // on Windows, so let's only check for Itanium guard variables. + bool is_guard_var = isGuardVariableSymbol(result_name, /*MS ABI*/ false); + + if (result_name.contains("$__lldb_expr_result_ptr") && !is_guard_var) { + found_result = true; + m_result_is_pointer = true; + break; + } + + if (result_name.contains("$__lldb_expr_result") && !is_guard_var) { + found_result = true; + m_result_is_pointer = false; + break; + } + } + + if (!found_result) { + LLDB_LOG(log, "Couldn't find result variable"); + + return true; + } + + LLDB_LOG(log, "Result name: \"{0}\"", result_name); + + Value *result_value = m_module->getNamedValue(result_name); + + if (!result_value) { + LLDB_LOG(log, "Result variable had no data"); + + m_error_stream.Format("Internal error [IRForTarget]: Result variable's " + "name ({0}) exists, but not its definition\n", + result_name); + + return false; + } + + LLDB_LOG(log, "Found result in the IR: \"{0}\"", + PrintValue(result_value, false)); + + GlobalVariable *result_global = dyn_cast<GlobalVariable>(result_value); + + if (!result_global) { + LLDB_LOG(log, "Result variable isn't a GlobalVariable"); + + m_error_stream.Format("Internal error [IRForTarget]: Result variable ({0}) " + "is defined, but is not a global variable\n", + result_name); + + return false; + } + + clang::NamedDecl *result_decl = DeclForGlobal(result_global); + if (!result_decl) { + LLDB_LOG(log, "Result variable doesn't have a corresponding Decl"); + + m_error_stream.Format("Internal error [IRForTarget]: Result variable ({0}) " + "does not have a corresponding Clang entity\n", + result_name); + + return false; + } + + if (log) { + std::string decl_desc_str; + raw_string_ostream decl_desc_stream(decl_desc_str); + result_decl->print(decl_desc_stream); + decl_desc_stream.flush(); + + LLDB_LOG(log, "Found result decl: \"{0}\"", decl_desc_str); + } + + clang::VarDecl *result_var = dyn_cast<clang::VarDecl>(result_decl); + if (!result_var) { + LLDB_LOG(log, "Result variable Decl isn't a VarDecl"); + + m_error_stream.Format("Internal error [IRForTarget]: Result variable " + "({0})'s corresponding Clang entity isn't a " + "variable\n", + result_name); + + return false; + } + + // Get the next available result name from m_decl_map and create the + // persistent variable for it + + // If the result is an Lvalue, it is emitted as a pointer; see + // ASTResultSynthesizer::SynthesizeBodyResult. + if (m_result_is_pointer) { + clang::QualType pointer_qual_type = result_var->getType(); + const clang::Type *pointer_type = pointer_qual_type.getTypePtr(); + + const clang::PointerType *pointer_pointertype = + pointer_type->getAs<clang::PointerType>(); + const clang::ObjCObjectPointerType *pointer_objcobjpointertype = + pointer_type->getAs<clang::ObjCObjectPointerType>(); + + if (pointer_pointertype) { + clang::QualType element_qual_type = pointer_pointertype->getPointeeType(); + + m_result_type = lldb_private::TypeFromParser( + element_qual_type.getAsOpaquePtr(), + lldb_private::ClangASTContext::GetASTContext( + &result_decl->getASTContext())); + } else if (pointer_objcobjpointertype) { + clang::QualType element_qual_type = + clang::QualType(pointer_objcobjpointertype->getObjectType(), 0); + + m_result_type = lldb_private::TypeFromParser( + element_qual_type.getAsOpaquePtr(), + lldb_private::ClangASTContext::GetASTContext( + &result_decl->getASTContext())); + } else { + LLDB_LOG(log, "Expected result to have pointer type, but it did not"); + + m_error_stream.Format("Internal error [IRForTarget]: Lvalue result ({0}) " + "is not a pointer variable\n", + result_name); + + return false; + } + } else { + m_result_type = lldb_private::TypeFromParser( + result_var->getType().getAsOpaquePtr(), + lldb_private::ClangASTContext::GetASTContext( + &result_decl->getASTContext())); + } + + lldb::TargetSP target_sp(m_execution_unit.GetTarget()); + lldb_private::ExecutionContext exe_ctx(target_sp, true); + llvm::Optional<uint64_t> bit_size = + m_result_type.GetBitSize(exe_ctx.GetBestExecutionContextScope()); + if (!bit_size) { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + LLDB_LOG(log, "Result type has unknown size"); + + m_error_stream.Printf("Error [IRForTarget]: Size of result type '%s' " + "couldn't be determined\n", + type_desc_stream.GetData()); + return false; + } + + if (log) { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + LLDB_LOG(log, "Result decl type: \"{0}\"", type_desc_stream.GetData()); + } + + m_result_name = lldb_private::ConstString("$RESULT_NAME"); + + LLDB_LOG(log, "Creating a new result global: \"{0}\" with size {1}", + m_result_name, m_result_type.GetByteSize(nullptr).getValueOr(0)); + + // Construct a new result global and set up its metadata + + GlobalVariable *new_result_global = new GlobalVariable( + (*m_module), result_global->getType()->getElementType(), + false, /* not constant */ + GlobalValue::ExternalLinkage, nullptr, /* no initializer */ + m_result_name.GetCString()); + + // It's too late in compilation to create a new VarDecl for this, but we + // don't need to. We point the metadata at the old VarDecl. This creates an + // odd anomaly: a variable with a Value whose name is something like $0 and a + // Decl whose name is $__lldb_expr_result. This condition is handled in + // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is + // fixed up. + + ConstantInt *new_constant_int = + ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()), + reinterpret_cast<uintptr_t>(result_decl), false); + + llvm::Metadata *values[2]; + values[0] = ConstantAsMetadata::get(new_result_global); + values[1] = ConstantAsMetadata::get(new_constant_int); + + ArrayRef<Metadata *> value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + NamedMDNode *named_metadata = + m_module->getNamedMetadata("clang.global.decl.ptrs"); + named_metadata->addOperand(persistent_global_md); + + LLDB_LOG(log, "Replacing \"{0}\" with \"{1}\"", PrintValue(result_global), + PrintValue(new_result_global)); + + if (result_global->use_empty()) { + // We need to synthesize a store for this variable, because otherwise + // there's nothing to put into its equivalent persistent variable. + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg()); + + if (!first_entry_instruction) + return false; + + if (!result_global->hasInitializer()) { + LLDB_LOG(log, "Couldn't find initializer for unused variable"); + + m_error_stream.Format("Internal error [IRForTarget]: Result variable " + "({0}) has no writes and no initializer\n", + result_name); + + return false; + } + + Constant *initializer = result_global->getInitializer(); + + StoreInst *synthesized_store = + new StoreInst(initializer, new_result_global, first_entry_instruction); + + LLDB_LOG(log, "Synthesized result store \"{0}\"\n", + PrintValue(synthesized_store)); + } else { + result_global->replaceAllUsesWith(new_result_global); + } + + if (!m_decl_map->AddPersistentVariable( + result_decl, m_result_name, m_result_type, true, m_result_is_pointer)) + return false; + + result_global->eraseFromParent(); + + return true; +} + +bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str, + llvm::GlobalVariable *cstr) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + Type *ns_str_ty = ns_str->getType(); + + Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + Type *i32_ty = Type::getInt32Ty(m_module->getContext()); + Type *i8_ty = Type::getInt8Ty(m_module->getContext()); + + if (!m_CFStringCreateWithBytes) { + lldb::addr_t CFStringCreateWithBytes_addr; + + static lldb_private::ConstString g_CFStringCreateWithBytes_str( + "CFStringCreateWithBytes"); + + bool missing_weak = false; + CFStringCreateWithBytes_addr = + m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str, + missing_weak); + if (CFStringCreateWithBytes_addr == LLDB_INVALID_ADDRESS || missing_weak) { + log->PutCString("Couldn't find CFStringCreateWithBytes in the target"); + + m_error_stream.Printf("Error [IRForTarget]: Rewriting an Objective-C " + "constant string requires " + "CFStringCreateWithBytes\n"); + + return false; + } + + LLDB_LOG(log, "Found CFStringCreateWithBytes at {0}", + CFStringCreateWithBytes_addr); + + // Build the function type: + // + // CFStringRef CFStringCreateWithBytes ( + // CFAllocatorRef alloc, + // const UInt8 *bytes, + // CFIndex numBytes, + // CFStringEncoding encoding, + // Boolean isExternalRepresentation + // ); + // + // We make the following substitutions: + // + // CFStringRef -> i8* + // CFAllocatorRef -> i8* + // UInt8 * -> i8* + // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its + // pointer size for now) CFStringEncoding -> i32 Boolean -> i8 + + Type *arg_type_array[5]; + + arg_type_array[0] = i8_ptr_ty; + arg_type_array[1] = i8_ptr_ty; + arg_type_array[2] = m_intptr_ty; + arg_type_array[3] = i32_ty; + arg_type_array[4] = i8_ty; + + ArrayRef<Type *> CFSCWB_arg_types(arg_type_array, 5); + + llvm::FunctionType *CFSCWB_ty = + FunctionType::get(ns_str_ty, CFSCWB_arg_types, false); + + // Build the constant containing the pointer to the function + PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty); + Constant *CFSCWB_addr_int = + ConstantInt::get(m_intptr_ty, CFStringCreateWithBytes_addr, false); + m_CFStringCreateWithBytes = { + CFSCWB_ty, ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty)}; + } + + ConstantDataSequential *string_array = nullptr; + + if (cstr) + string_array = dyn_cast<ConstantDataSequential>(cstr->getInitializer()); + + Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty); + Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty) + : Constant::getNullValue(i8_ptr_ty); + Constant *numBytes_arg = ConstantInt::get( + m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false); + int encoding_flags = 0; + switch (cstr ? string_array->getElementByteSize() : 1) { + case 1: + encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */ + break; + case 2: + encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */ + break; + case 4: + encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */ + break; + default: + encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */ + LLDB_LOG(log, "Encountered an Objective-C constant string with unusual " + "element size {0}", + string_array->getElementByteSize()); + } + Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false); + Constant *isExternal_arg = + ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */ + + Value *argument_array[5]; + + argument_array[0] = alloc_arg; + argument_array[1] = bytes_arg; + argument_array[2] = numBytes_arg; + argument_array[3] = encoding_arg; + argument_array[4] = isExternal_arg; + + ArrayRef<Value *> CFSCWB_arguments(argument_array, 5); + + FunctionValueCache CFSCWB_Caller( + [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * { + return CallInst::Create( + m_CFStringCreateWithBytes, CFSCWB_arguments, + "CFStringCreateWithBytes", + llvm::cast<Instruction>( + m_entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller, m_entry_instruction_finder, + m_error_stream)) { + LLDB_LOG(log, "Couldn't replace the NSString with the result of the call"); + + m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an " + "Objective-C constant string with a dynamic " + "string\n"); + + return false; + } + + ns_str->eraseFromParent(); + + return true; +} + +bool IRForTarget::RewriteObjCConstStrings() { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable(); + + for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) { + llvm::StringRef value_name = value_symbol.first(); + + if (value_name.contains("_unnamed_cfstring_")) { + Value *nsstring_value = value_symbol.second; + + GlobalVariable *nsstring_global = + dyn_cast<GlobalVariable>(nsstring_value); + + if (!nsstring_global) { + LLDB_LOG(log, "NSString variable is not a GlobalVariable"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string is not a global variable\n"); + + return false; + } + + if (!nsstring_global->hasInitializer()) { + LLDB_LOG(log, "NSString variable does not have an initializer"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string does not have an initializer\n"); + + return false; + } + + ConstantStruct *nsstring_struct = + dyn_cast<ConstantStruct>(nsstring_global->getInitializer()); + + if (!nsstring_struct) { + LLDB_LOG(log, + "NSString variable's initializer is not a ConstantStruct"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string is not a structure constant\n"); + + return false; + } + + // We expect the following structure: + // + // struct { + // int *isa; + // int flags; + // char *str; + // long length; + // }; + + if (nsstring_struct->getNumOperands() != 4) { + + LLDB_LOG(log, + "NSString variable's initializer structure has an " + "unexpected number of members. Should be 4, is {0}", + nsstring_struct->getNumOperands()); + + m_error_stream.Printf("Internal error [IRForTarget]: The struct for an " + "Objective-C constant string is not as " + "expected\n"); + + return false; + } + + Constant *nsstring_member = nsstring_struct->getOperand(2); + + if (!nsstring_member) { + LLDB_LOG(log, "NSString initializer's str element was empty"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string does not have a string " + "initializer\n"); + + return false; + } + + ConstantExpr *nsstring_expr = dyn_cast<ConstantExpr>(nsstring_member); + + if (!nsstring_expr) { + LLDB_LOG(log, + "NSString initializer's str element is not a ConstantExpr"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string's string initializer is not " + "constant\n"); + + return false; + } + + GlobalVariable *cstr_global = nullptr; + + if (nsstring_expr->getOpcode() == Instruction::GetElementPtr) { + Constant *nsstring_cstr = nsstring_expr->getOperand(0); + cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr); + } else if (nsstring_expr->getOpcode() == Instruction::BitCast) { + Constant *nsstring_cstr = nsstring_expr->getOperand(0); + cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr); + } + + if (!cstr_global) { + LLDB_LOG(log, + "NSString initializer's str element is not a GlobalVariable"); + + m_error_stream.Printf("Internal error [IRForTarget]: Unhandled" + "constant string initializer\n"); + + return false; + } + + if (!cstr_global->hasInitializer()) { + LLDB_LOG(log, "NSString initializer's str element does not have an " + "initializer"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string's string initializer doesn't " + "point to initialized data\n"); + + return false; + } + + /* + if (!cstr_array) + { + if (log) + log->PutCString("NSString initializer's str element is not a + ConstantArray"); + + if (m_error_stream) + m_error_stream.Printf("Internal error [IRForTarget]: An + Objective-C constant string's string initializer doesn't point to an + array\n"); + + return false; + } + + if (!cstr_array->isCString()) + { + if (log) + log->PutCString("NSString initializer's str element is not a C + string array"); + + if (m_error_stream) + m_error_stream.Printf("Internal error [IRForTarget]: An + Objective-C constant string's string initializer doesn't point to a C + string\n"); + + return false; + } + */ + + ConstantDataArray *cstr_array = + dyn_cast<ConstantDataArray>(cstr_global->getInitializer()); + + if (cstr_array) + LLDB_LOG(log, "Found NSString constant {0}, which contains \"{1}\"", + value_name, cstr_array->getAsString()); + else + LLDB_LOG(log, "Found NSString constant {0}, which contains \"\"", + value_name); + + if (!cstr_array) + cstr_global = nullptr; + + if (!RewriteObjCConstString(nsstring_global, cstr_global)) { + LLDB_LOG(log, "Error rewriting the constant string"); + + // We don't print an error message here because RewriteObjCConstString + // has done so for us. + + return false; + } + } + } + + for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) { + llvm::StringRef value_name = value_symbol.first(); + + if (value_name == "__CFConstantStringClassReference") { + GlobalVariable *gv = dyn_cast<GlobalVariable>(value_symbol.second); + + if (!gv) { + LLDB_LOG(log, + "__CFConstantStringClassReference is not a global variable"); + + m_error_stream.Printf("Internal error [IRForTarget]: Found a " + "CFConstantStringClassReference, but it is not a " + "global object\n"); + + return false; + } + + gv->eraseFromParent(); + + break; + } + } + + return true; +} + +static bool IsObjCSelectorRef(Value *value) { + GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value); + + return !(!global_variable || !global_variable->hasName() || + !global_variable->getName().startswith("OBJC_SELECTOR_REFERENCES_")); +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::RewriteObjCSelector(Instruction *selector_load) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LoadInst *load = dyn_cast<LoadInst>(selector_load); + + if (!load) + return false; + + // Unpack the message name from the selector. In LLVM IR, an objc_msgSend + // gets represented as + // + // %tmp = load i8** @"OBJC_SELECTOR_REFERENCES_" ; <i8*> %call = call + // i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*> + // + // where %obj is the object pointer and %tmp is the selector. + // + // @"OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called + // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". + // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string. + + // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) + // and get the string from its target + + GlobalVariable *_objc_selector_references_ = + dyn_cast<GlobalVariable>(load->getPointerOperand()); + + if (!_objc_selector_references_ || + !_objc_selector_references_->hasInitializer()) + return false; + + Constant *osr_initializer = _objc_selector_references_->getInitializer(); + + ConstantExpr *osr_initializer_expr = dyn_cast<ConstantExpr>(osr_initializer); + + if (!osr_initializer_expr || + osr_initializer_expr->getOpcode() != Instruction::GetElementPtr) + return false; + + Value *osr_initializer_base = osr_initializer_expr->getOperand(0); + + if (!osr_initializer_base) + return false; + + // Find the string's initializer (a ConstantArray) and get the string from it + + GlobalVariable *_objc_meth_var_name_ = + dyn_cast<GlobalVariable>(osr_initializer_base); + + if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer()) + return false; + + Constant *omvn_initializer = _objc_meth_var_name_->getInitializer(); + + ConstantDataArray *omvn_initializer_array = + dyn_cast<ConstantDataArray>(omvn_initializer); + + if (!omvn_initializer_array->isString()) + return false; + + std::string omvn_initializer_string = omvn_initializer_array->getAsString(); + + LLDB_LOG(log, "Found Objective-C selector reference \"{0}\"", + omvn_initializer_string); + + // Construct a call to sel_registerName + + if (!m_sel_registerName) { + lldb::addr_t sel_registerName_addr; + + bool missing_weak = false; + static lldb_private::ConstString g_sel_registerName_str("sel_registerName"); + sel_registerName_addr = m_execution_unit.FindSymbol(g_sel_registerName_str, + missing_weak); + if (sel_registerName_addr == LLDB_INVALID_ADDRESS || missing_weak) + return false; + + LLDB_LOG(log, "Found sel_registerName at {0}", sel_registerName_addr); + + // Build the function type: struct objc_selector + // *sel_registerName(uint8_t*) + + // The below code would be "more correct," but in actuality what's required + // is uint8_t* + // Type *sel_type = StructType::get(m_module->getContext()); + // Type *sel_ptr_type = PointerType::getUnqual(sel_type); + Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext()); + + Type *type_array[1]; + + type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext()); + + ArrayRef<Type *> srN_arg_types(type_array, 1); + + llvm::FunctionType *srN_type = + FunctionType::get(sel_ptr_type, srN_arg_types, false); + + // Build the constant containing the pointer to the function + PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type); + Constant *srN_addr_int = + ConstantInt::get(m_intptr_ty, sel_registerName_addr, false); + m_sel_registerName = {srN_type, + ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty)}; + } + + Value *argument_array[1]; + + Constant *omvn_pointer = ConstantExpr::getBitCast( + _objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext())); + + argument_array[0] = omvn_pointer; + + ArrayRef<Value *> srN_arguments(argument_array, 1); + + CallInst *srN_call = CallInst::Create(m_sel_registerName, srN_arguments, + "sel_registerName", selector_load); + + // Replace the load with the call in all users + + selector_load->replaceAllUsesWith(srN_call); + + selector_load->eraseFromParent(); + + return true; +} + +bool IRForTarget::RewriteObjCSelectors(BasicBlock &basic_block) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + InstrList selector_loads; + + for (Instruction &inst : basic_block) { + if (LoadInst *load = dyn_cast<LoadInst>(&inst)) + if (IsObjCSelectorRef(load->getPointerOperand())) + selector_loads.push_back(&inst); + } + + for (Instruction *inst : selector_loads) { + if (!RewriteObjCSelector(inst)) { + m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a " + "static reference to an Objective-C selector to a " + "dynamic reference\n"); + + LLDB_LOG(log, "Couldn't rewrite a reference to an Objective-C selector"); + + return false; + } + } + + return true; +} + +static bool IsObjCClassReference(Value *value) { + GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value); + + return !(!global_variable || !global_variable->hasName() || + !global_variable->getName().startswith("OBJC_CLASS_REFERENCES_")); +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::RewriteObjCClassReference(Instruction *class_load) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LoadInst *load = dyn_cast<LoadInst>(class_load); + + if (!load) + return false; + + // Unpack the class name from the reference. In LLVM IR, a reference to an + // Objective-C class gets represented as + // + // %tmp = load %struct._objc_class*, + // %struct._objc_class** @OBJC_CLASS_REFERENCES_, align 4 + // + // @"OBJC_CLASS_REFERENCES_ is a bitcast of a character array called + // @OBJC_CLASS_NAME_. @OBJC_CLASS_NAME contains the string. + + // Find the pointer's initializer (a ConstantExpr with opcode BitCast) and + // get the string from its target + + GlobalVariable *_objc_class_references_ = + dyn_cast<GlobalVariable>(load->getPointerOperand()); + + if (!_objc_class_references_ || + !_objc_class_references_->hasInitializer()) + return false; + + Constant *ocr_initializer = _objc_class_references_->getInitializer(); + + ConstantExpr *ocr_initializer_expr = dyn_cast<ConstantExpr>(ocr_initializer); + + if (!ocr_initializer_expr || + ocr_initializer_expr->getOpcode() != Instruction::BitCast) + return false; + + Value *ocr_initializer_base = ocr_initializer_expr->getOperand(0); + + if (!ocr_initializer_base) + return false; + + // Find the string's initializer (a ConstantArray) and get the string from it + + GlobalVariable *_objc_class_name_ = + dyn_cast<GlobalVariable>(ocr_initializer_base); + + if (!_objc_class_name_ || !_objc_class_name_->hasInitializer()) + return false; + + Constant *ocn_initializer = _objc_class_name_->getInitializer(); + + ConstantDataArray *ocn_initializer_array = + dyn_cast<ConstantDataArray>(ocn_initializer); + + if (!ocn_initializer_array->isString()) + return false; + + std::string ocn_initializer_string = ocn_initializer_array->getAsString(); + + LLDB_LOG(log, "Found Objective-C class reference \"{0}\"", + ocn_initializer_string); + + // Construct a call to objc_getClass + + if (!m_objc_getClass) { + lldb::addr_t objc_getClass_addr; + + bool missing_weak = false; + static lldb_private::ConstString g_objc_getClass_str("objc_getClass"); + objc_getClass_addr = m_execution_unit.FindSymbol(g_objc_getClass_str, + missing_weak); + if (objc_getClass_addr == LLDB_INVALID_ADDRESS || missing_weak) + return false; + + LLDB_LOG(log, "Found objc_getClass at {0}", objc_getClass_addr); + + // Build the function type: %struct._objc_class *objc_getClass(i8*) + + Type *class_type = load->getType(); + Type *type_array[1]; + type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext()); + + ArrayRef<Type *> ogC_arg_types(type_array, 1); + + llvm::FunctionType *ogC_type = + FunctionType::get(class_type, ogC_arg_types, false); + + // Build the constant containing the pointer to the function + PointerType *ogC_ptr_ty = PointerType::getUnqual(ogC_type); + Constant *ogC_addr_int = + ConstantInt::get(m_intptr_ty, objc_getClass_addr, false); + m_objc_getClass = {ogC_type, + ConstantExpr::getIntToPtr(ogC_addr_int, ogC_ptr_ty)}; + } + + Value *argument_array[1]; + + Constant *ocn_pointer = ConstantExpr::getBitCast( + _objc_class_name_, Type::getInt8PtrTy(m_module->getContext())); + + argument_array[0] = ocn_pointer; + + ArrayRef<Value *> ogC_arguments(argument_array, 1); + + CallInst *ogC_call = CallInst::Create(m_objc_getClass, ogC_arguments, + "objc_getClass", class_load); + + // Replace the load with the call in all users + + class_load->replaceAllUsesWith(ogC_call); + + class_load->eraseFromParent(); + + return true; +} + +bool IRForTarget::RewriteObjCClassReferences(BasicBlock &basic_block) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + InstrList class_loads; + + for (Instruction &inst : basic_block) { + if (LoadInst *load = dyn_cast<LoadInst>(&inst)) + if (IsObjCClassReference(load->getPointerOperand())) + class_loads.push_back(&inst); + } + + for (Instruction *inst : class_loads) { + if (!RewriteObjCClassReference(inst)) { + m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a " + "static reference to an Objective-C class to a " + "dynamic reference\n"); + + LLDB_LOG(log, "Couldn't rewrite a reference to an Objective-C class"); + + return false; + } + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::RewritePersistentAlloc(llvm::Instruction *persistent_alloc) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + AllocaInst *alloc = dyn_cast<AllocaInst>(persistent_alloc); + + MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr"); + + if (!alloc_md || !alloc_md->getNumOperands()) + return false; + + ConstantInt *constant_int = + mdconst::dyn_extract<ConstantInt>(alloc_md->getOperand(0)); + + if (!constant_int) + return false; + + // We attempt to register this as a new persistent variable with the DeclMap. + + uintptr_t ptr = constant_int->getZExtValue(); + + clang::VarDecl *decl = reinterpret_cast<clang::VarDecl *>(ptr); + + lldb_private::TypeFromParser result_decl_type( + decl->getType().getAsOpaquePtr(), + lldb_private::ClangASTContext::GetASTContext(&decl->getASTContext())); + + StringRef decl_name(decl->getName()); + lldb_private::ConstString persistent_variable_name(decl_name.data(), + decl_name.size()); + if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name, + result_decl_type, false, false)) + return false; + + GlobalVariable *persistent_global = new GlobalVariable( + (*m_module), alloc->getType(), false, /* not constant */ + GlobalValue::ExternalLinkage, nullptr, /* no initializer */ + alloc->getName().str()); + + // What we're going to do here is make believe this was a regular old + // external variable. That means we need to make the metadata valid. + + NamedMDNode *named_metadata = + m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs"); + + llvm::Metadata *values[2]; + values[0] = ConstantAsMetadata::get(persistent_global); + values[1] = ConstantAsMetadata::get(constant_int); + + ArrayRef<llvm::Metadata *> value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + named_metadata->addOperand(persistent_global_md); + + // Now, since the variable is a pointer variable, we will drop in a load of + // that pointer variable. + + LoadInst *persistent_load = new LoadInst(persistent_global, "", alloc); + + LLDB_LOG(log, "Replacing \"{0}\" with \"{1}\"", PrintValue(alloc), + PrintValue(persistent_load)); + + alloc->replaceAllUsesWith(persistent_load); + alloc->eraseFromParent(); + + return true; +} + +bool IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) { + if (!m_resolve_vars) + return true; + + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + InstrList pvar_allocs; + + for (Instruction &inst : basic_block) { + + if (AllocaInst *alloc = dyn_cast<AllocaInst>(&inst)) { + llvm::StringRef alloc_name = alloc->getName(); + + if (alloc_name.startswith("$") && !alloc_name.startswith("$__lldb")) { + if (alloc_name.find_first_of("0123456789") == 1) { + LLDB_LOG(log, "Rejecting a numeric persistent variable."); + + m_error_stream.Printf("Error [IRForTarget]: Names starting with $0, " + "$1, ... are reserved for use as result " + "names\n"); + + return false; + } + + pvar_allocs.push_back(alloc); + } + } + } + + for (Instruction *inst : pvar_allocs) { + if (!RewritePersistentAlloc(inst)) { + m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite " + "the creation of a persistent variable\n"); + + LLDB_LOG(log, "Couldn't rewrite the creation of a persistent variable"); + + return false; + } + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::MaybeHandleVariable(Value *llvm_value_ptr) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOG(log, "MaybeHandleVariable ({0})", PrintValue(llvm_value_ptr)); + + if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(llvm_value_ptr)) { + switch (constant_expr->getOpcode()) { + default: + break; + case Instruction::GetElementPtr: + case Instruction::BitCast: + Value *s = constant_expr->getOperand(0); + if (!MaybeHandleVariable(s)) + return false; + } + } else if (GlobalVariable *global_variable = + dyn_cast<GlobalVariable>(llvm_value_ptr)) { + if (!GlobalValue::isExternalLinkage(global_variable->getLinkage())) + return true; + + clang::NamedDecl *named_decl = DeclForGlobal(global_variable); + + if (!named_decl) { + if (IsObjCSelectorRef(llvm_value_ptr)) + return true; + + if (!global_variable->hasExternalLinkage()) + return true; + + LLDB_LOG(log, "Found global variable \"{0}\" without metadata", + global_variable->getName()); + + return false; + } + + llvm::StringRef name(named_decl->getName()); + + clang::ValueDecl *value_decl = dyn_cast<clang::ValueDecl>(named_decl); + if (value_decl == nullptr) + return false; + + lldb_private::CompilerType compiler_type( + lldb_private::ClangASTContext::GetASTContext( + &value_decl->getASTContext()), + value_decl->getType().getAsOpaquePtr()); + + const Type *value_type = nullptr; + + if (name.startswith("$")) { + // The $__lldb_expr_result name indicates the return value has allocated + // as a static variable. Per the comment at + // ASTResultSynthesizer::SynthesizeBodyResult, accesses to this static + // variable need to be redirected to the result of dereferencing a + // pointer that is passed in as one of the arguments. + // + // Consequently, when reporting the size of the type, we report a pointer + // type pointing to the type of $__lldb_expr_result, not the type itself. + // + // We also do this for any user-declared persistent variables. + compiler_type = compiler_type.GetPointerType(); + value_type = PointerType::get(global_variable->getType(), 0); + } else { + value_type = global_variable->getType(); + } + + llvm::Optional<uint64_t> value_size = compiler_type.GetByteSize(nullptr); + if (!value_size) + return false; + llvm::Optional<size_t> opt_alignment = compiler_type.GetTypeBitAlign(nullptr); + if (!opt_alignment) + return false; + lldb::offset_t value_alignment = (*opt_alignment + 7ull) / 8ull; + + LLDB_LOG(log, + "Type of \"{0}\" is [clang \"{1}\", llvm \"{2}\"] [size {3}, " + "align {4}]", + name, + lldb_private::ClangUtil::GetQualType(compiler_type).getAsString(), + PrintType(value_type), *value_size, value_alignment); + + if (named_decl) + m_decl_map->AddValueToStruct(named_decl, lldb_private::ConstString(name), + llvm_value_ptr, *value_size, + value_alignment); + } else if (dyn_cast<llvm::Function>(llvm_value_ptr)) { + LLDB_LOG(log, "Function pointers aren't handled right now"); + + return false; + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::HandleSymbol(Value *symbol) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + lldb_private::ConstString name(symbol->getName().str().c_str()); + + lldb::addr_t symbol_addr = + m_decl_map->GetSymbolAddress(name, lldb::eSymbolTypeAny); + + if (symbol_addr == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "Symbol \"{0}\" had no address", name); + + return false; + } + + LLDB_LOG(log, "Found \"{0}\" at {1}", name, symbol_addr); + + Type *symbol_type = symbol->getType(); + + Constant *symbol_addr_int = ConstantInt::get(m_intptr_ty, symbol_addr, false); + + Value *symbol_addr_ptr = + ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type); + + LLDB_LOG(log, "Replacing {0} with {1}", PrintValue(symbol), + PrintValue(symbol_addr_ptr)); + + symbol->replaceAllUsesWith(symbol_addr_ptr); + + return true; +} + +bool IRForTarget::MaybeHandleCallArguments(CallInst *Old) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + LLDB_LOG(log, "MaybeHandleCallArguments({0})", PrintValue(Old)); + + for (unsigned op_index = 0, num_ops = Old->getNumArgOperands(); + op_index < num_ops; ++op_index) + // conservatively believe that this is a store + if (!MaybeHandleVariable(Old->getArgOperand(op_index))) { + m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite " + "one of the arguments of a function call.\n"); + + return false; + } + + return true; +} + +bool IRForTarget::HandleObjCClass(Value *classlist_reference) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + GlobalVariable *global_variable = + dyn_cast<GlobalVariable>(classlist_reference); + + if (!global_variable) + return false; + + Constant *initializer = global_variable->getInitializer(); + + if (!initializer) + return false; + + if (!initializer->hasName()) + return false; + + StringRef name(initializer->getName()); + lldb_private::ConstString name_cstr(name.str().c_str()); + lldb::addr_t class_ptr = + m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass); + + LLDB_LOG(log, "Found reference to Objective-C class {0} ({1})", name, + (unsigned long long)class_ptr); + + if (class_ptr == LLDB_INVALID_ADDRESS) + return false; + + if (global_variable->use_empty()) + return false; + + SmallVector<LoadInst *, 2> load_instructions; + + for (llvm::User *u : global_variable->users()) { + if (LoadInst *load_instruction = dyn_cast<LoadInst>(u)) + load_instructions.push_back(load_instruction); + } + + if (load_instructions.empty()) + return false; + + Constant *class_addr = ConstantInt::get(m_intptr_ty, (uint64_t)class_ptr); + + for (LoadInst *load_instruction : load_instructions) { + Constant *class_bitcast = + ConstantExpr::getIntToPtr(class_addr, load_instruction->getType()); + + load_instruction->replaceAllUsesWith(class_bitcast); + + load_instruction->eraseFromParent(); + } + + return true; +} + +bool IRForTarget::RemoveCXAAtExit(BasicBlock &basic_block) { + std::vector<CallInst *> calls_to_remove; + + for (Instruction &inst : basic_block) { + CallInst *call = dyn_cast<CallInst>(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (!call) + continue; + + bool remove = false; + + llvm::Function *func = call->getCalledFunction(); + + if (func && func->getName() == "__cxa_atexit") + remove = true; + + llvm::Value *val = call->getCalledValue(); + + if (val && val->getName() == "__cxa_atexit") + remove = true; + + if (remove) + calls_to_remove.push_back(call); + } + + for (CallInst *ci : calls_to_remove) + ci->eraseFromParent(); + + return true; +} + +bool IRForTarget::ResolveCalls(BasicBlock &basic_block) { + // Prepare the current basic block for execution in the remote process + + for (Instruction &inst : basic_block) { + CallInst *call = dyn_cast<CallInst>(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (call && !MaybeHandleCallArguments(call)) + return false; + } + + return true; +} + +bool IRForTarget::ResolveExternals(Function &llvm_function) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + for (GlobalVariable &global_var : m_module->globals()) { + llvm::StringRef global_name = global_var.getName(); + + LLDB_LOG(log, "Examining {0}, DeclForGlobalValue returns {1}", global_name, + static_cast<void *>(DeclForGlobal(&global_var))); + + if (global_name.startswith("OBJC_IVAR")) { + if (!HandleSymbol(&global_var)) { + m_error_stream.Format("Error [IRForTarget]: Couldn't find Objective-C " + "indirect ivar symbol {0}\n", + global_name); + + return false; + } + } else if (global_name.contains("OBJC_CLASSLIST_REFERENCES_$")) { + if (!HandleObjCClass(&global_var)) { + m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class " + "for an Objective-C static method call\n"); + + return false; + } + } else if (global_name.contains("OBJC_CLASSLIST_SUP_REFS_$")) { + if (!HandleObjCClass(&global_var)) { + m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class " + "for an Objective-C static method call\n"); + + return false; + } + } else if (DeclForGlobal(&global_var)) { + if (!MaybeHandleVariable(&global_var)) { + m_error_stream.Format("Internal error [IRForTarget]: Couldn't rewrite " + "external variable {0}\n", + global_name); + + return false; + } + } + } + + return true; +} + +static bool isGuardVariableRef(Value *V) { + Constant *Old = dyn_cast<Constant>(V); + + if (!Old) + return false; + + if (auto CE = dyn_cast<ConstantExpr>(V)) { + if (CE->getOpcode() != Instruction::BitCast) + return false; + + Old = CE->getOperand(0); + } + + GlobalVariable *GV = dyn_cast<GlobalVariable>(Old); + + if (!GV || !GV->hasName() || !isGuardVariableSymbol(GV->getName())) + return false; + + return true; +} + +void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction *guard_load) { + Constant *zero(Constant::getNullValue(guard_load->getType())); + guard_load->replaceAllUsesWith(zero); + guard_load->eraseFromParent(); +} + +static void ExciseGuardStore(Instruction *guard_store) { + guard_store->eraseFromParent(); +} + +bool IRForTarget::RemoveGuards(BasicBlock &basic_block) { + // Eliminate any reference to guard variables found. + + InstrList guard_loads; + InstrList guard_stores; + + for (Instruction &inst : basic_block) { + + if (LoadInst *load = dyn_cast<LoadInst>(&inst)) + if (isGuardVariableRef(load->getPointerOperand())) + guard_loads.push_back(&inst); + + if (StoreInst *store = dyn_cast<StoreInst>(&inst)) + if (isGuardVariableRef(store->getPointerOperand())) + guard_stores.push_back(&inst); + } + + for (Instruction *inst : guard_loads) + TurnGuardLoadIntoZero(inst); + + for (Instruction *inst : guard_stores) + ExciseGuardStore(inst); + + return true; +} + +// This function does not report errors; its callers are responsible. +bool IRForTarget::UnfoldConstant(Constant *old_constant, + llvm::Function *llvm_function, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder, + lldb_private::Stream &error_stream) { + SmallVector<User *, 16> users; + + // We do this because the use list might change, invalidating our iterator. + // Much better to keep a work list ourselves. + for (llvm::User *u : old_constant->users()) + users.push_back(u); + + for (size_t i = 0; i < users.size(); ++i) { + User *user = users[i]; + + if (Constant *constant = dyn_cast<Constant>(user)) { + // synthesize a new non-constant equivalent of the constant + + if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) { + switch (constant_expr->getOpcode()) { + default: + error_stream.Printf("error [IRForTarget internal]: Unhandled " + "constant expression type: \"%s\"", + PrintValue(constant_expr).c_str()); + return false; + case Instruction::BitCast: { + FunctionValueCache bit_cast_maker( + [&value_maker, &entry_instruction_finder, old_constant, + constant_expr](llvm::Function *function) -> llvm::Value * { + // UnaryExpr + // OperandList[0] is value + + if (constant_expr->getOperand(0) != old_constant) + return constant_expr; + + return new BitCastInst( + value_maker.GetValue(function), constant_expr->getType(), + "", llvm::cast<Instruction>( + entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, llvm_function, bit_cast_maker, + entry_instruction_finder, error_stream)) + return false; + } break; + case Instruction::GetElementPtr: { + // GetElementPtrConstantExpr + // OperandList[0] is base + // OperandList[1]... are indices + + FunctionValueCache get_element_pointer_maker( + [&value_maker, &entry_instruction_finder, old_constant, + constant_expr](llvm::Function *function) -> llvm::Value * { + Value *ptr = constant_expr->getOperand(0); + + if (ptr == old_constant) + ptr = value_maker.GetValue(function); + + std::vector<Value *> index_vector; + + unsigned operand_index; + unsigned num_operands = constant_expr->getNumOperands(); + + for (operand_index = 1; operand_index < num_operands; + ++operand_index) { + Value *operand = constant_expr->getOperand(operand_index); + + if (operand == old_constant) + operand = value_maker.GetValue(function); + + index_vector.push_back(operand); + } + + ArrayRef<Value *> indices(index_vector); + + return GetElementPtrInst::Create( + nullptr, ptr, indices, "", + llvm::cast<Instruction>( + entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, llvm_function, + get_element_pointer_maker, + entry_instruction_finder, error_stream)) + return false; + } break; + } + } else { + error_stream.Printf( + "error [IRForTarget internal]: Unhandled constant type: \"%s\"", + PrintValue(constant).c_str()); + return false; + } + } else { + if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) { + if (llvm_function && inst->getParent()->getParent() != llvm_function) { + error_stream.PutCString("error: Capturing non-local variables in " + "expressions is unsupported.\n"); + return false; + } + inst->replaceUsesOfWith( + old_constant, value_maker.GetValue(inst->getParent()->getParent())); + } else { + error_stream.Printf( + "error [IRForTarget internal]: Unhandled non-constant type: \"%s\"", + PrintValue(user).c_str()); + return false; + } + } + } + + if (!isa<GlobalValue>(old_constant)) { + old_constant->destroyConstant(); + } + + return true; +} + +bool IRForTarget::ReplaceVariables(Function &llvm_function) { + if (!m_resolve_vars) + return true; + + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + m_decl_map->DoStructLayout(); + + LLDB_LOG(log, "Element arrangement:"); + + uint32_t num_elements; + uint32_t element_index; + + size_t size; + lldb::offset_t alignment; + + if (!m_decl_map->GetStructInfo(num_elements, size, alignment)) + return false; + + Function::arg_iterator iter(llvm_function.arg_begin()); + + if (iter == llvm_function.arg_end()) { + m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes no " + "arguments (should take at least a struct pointer)"); + + return false; + } + + Argument *argument = &*iter; + + if (argument->getName().equals("this")) { + ++iter; + + if (iter == llvm_function.arg_end()) { + m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only " + "'this' argument (should take a struct pointer " + "too)"); + + return false; + } + + argument = &*iter; + } else if (argument->getName().equals("self")) { + ++iter; + + if (iter == llvm_function.arg_end()) { + m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only " + "'self' argument (should take '_cmd' and a struct " + "pointer too)"); + + return false; + } + + if (!iter->getName().equals("_cmd")) { + m_error_stream.Format("Internal error [IRForTarget]: Wrapper takes '{0}' " + "after 'self' argument (should take '_cmd')", + iter->getName()); + + return false; + } + + ++iter; + + if (iter == llvm_function.arg_end()) { + m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only " + "'self' and '_cmd' arguments (should take a struct " + "pointer too)"); + + return false; + } + + argument = &*iter; + } + + if (!argument->getName().equals("$__lldb_arg")) { + m_error_stream.Format("Internal error [IRForTarget]: Wrapper takes an " + "argument named '{0}' instead of the struct pointer", + argument->getName()); + + return false; + } + + LLDB_LOG(log, "Arg: \"{0}\"", PrintValue(argument)); + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg()); + + if (!FirstEntryInstruction) { + m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find the " + "first instruction in the wrapper for use in " + "rewriting"); + + return false; + } + + LLVMContext &context(m_module->getContext()); + IntegerType *offset_type(Type::getInt32Ty(context)); + + if (!offset_type) { + m_error_stream.Printf( + "Internal error [IRForTarget]: Couldn't produce an offset type"); + + return false; + } + + for (element_index = 0; element_index < num_elements; ++element_index) { + const clang::NamedDecl *decl = nullptr; + Value *value = nullptr; + lldb::offset_t offset; + lldb_private::ConstString name; + + if (!m_decl_map->GetStructElement(decl, value, offset, name, + element_index)) { + m_error_stream.Printf( + "Internal error [IRForTarget]: Structure information is incomplete"); + + return false; + } + + LLDB_LOG(log, " \"{0}\" (\"{1}\") placed at {2}", name, + decl->getNameAsString(), offset); + + if (value) { + LLDB_LOG(log, " Replacing [{0}]", PrintValue(value)); + + FunctionValueCache body_result_maker( + [this, name, offset_type, offset, argument, + value](llvm::Function *function) -> llvm::Value * { + // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, + // in cases where the result variable is an rvalue, we have to + // synthesize a dereference of the appropriate structure entry in + // order to produce the static variable that the AST thinks it is + // accessing. + + llvm::Instruction *entry_instruction = llvm::cast<Instruction>( + m_entry_instruction_finder.GetValue(function)); + + ConstantInt *offset_int( + ConstantInt::get(offset_type, offset, true)); + GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create( + nullptr, argument, offset_int, "", entry_instruction); + + if (name == m_result_name && !m_result_is_pointer) { + BitCastInst *bit_cast = new BitCastInst( + get_element_ptr, value->getType()->getPointerTo(), "", + entry_instruction); + + LoadInst *load = new LoadInst(bit_cast, "", entry_instruction); + + return load; + } else { + BitCastInst *bit_cast = new BitCastInst( + get_element_ptr, value->getType(), "", entry_instruction); + + return bit_cast; + } + }); + + if (Constant *constant = dyn_cast<Constant>(value)) { + if (!UnfoldConstant(constant, &llvm_function, body_result_maker, + m_entry_instruction_finder, m_error_stream)) { + return false; + } + } else if (Instruction *instruction = dyn_cast<Instruction>(value)) { + if (instruction->getParent()->getParent() != &llvm_function) { + m_error_stream.PutCString("error: Capturing non-local variables in " + "expressions is unsupported.\n"); + return false; + } + value->replaceAllUsesWith( + body_result_maker.GetValue(instruction->getParent()->getParent())); + } else { + LLDB_LOG(log, "Unhandled non-constant type: \"{0}\"", + PrintValue(value)); + return false; + } + + if (GlobalVariable *var = dyn_cast<GlobalVariable>(value)) + var->eraseFromParent(); + } + } + + LLDB_LOG(log, "Total structure [align {0}, size {1}]", (int64_t)alignment, + (uint64_t)size); + + return true; +} + +bool IRForTarget::runOnModule(Module &llvm_module) { + lldb_private::Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + m_module = &llvm_module; + m_target_data.reset(new DataLayout(m_module)); + m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(), + m_target_data->getPointerSizeInBits()); + + if (log) { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, nullptr); + + oss.flush(); + + LLDB_LOG(log, "Module as passed in to IRForTarget: \n\"{0}\"", s); + } + + Function *const main_function = + m_func_name.IsEmpty() ? nullptr + : m_module->getFunction(m_func_name.GetStringRef()); + + if (!m_func_name.IsEmpty() && !main_function) { + LLDB_LOG(log, "Couldn't find \"{0}()\" in the module", m_func_name); + + m_error_stream.Format("Internal error [IRForTarget]: Couldn't find wrapper " + "'{0}' in the module", + m_func_name); + + return false; + } + + if (main_function) { + if (!FixFunctionLinkage(*main_function)) { + LLDB_LOG(log, "Couldn't fix the linkage for the function"); + + return false; + } + } + + llvm::Type *int8_ty = Type::getInt8Ty(m_module->getContext()); + + m_reloc_placeholder = new llvm::GlobalVariable( + (*m_module), int8_ty, false /* IsConstant */, + GlobalVariable::InternalLinkage, Constant::getNullValue(int8_ty), + "reloc_placeholder", nullptr /* InsertBefore */, + GlobalVariable::NotThreadLocal /* ThreadLocal */, 0 /* AddressSpace */); + + //////////////////////////////////////////////////////////// + // Replace $__lldb_expr_result with a persistent variable + // + + if (main_function) { + if (!CreateResultVariable(*main_function)) { + LLDB_LOG(log, "CreateResultVariable() failed"); + + // CreateResultVariable() reports its own errors, so we don't do so here + + return false; + } + } + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, nullptr); + + oss.flush(); + + LLDB_LOG(log, "Module after creating the result variable: \n\"{0}\"", s); + } + + for (llvm::Function &function : *m_module) { + for (BasicBlock &bb : function) { + if (!RemoveGuards(bb)) { + LLDB_LOG(log, "RemoveGuards() failed"); + + // RemoveGuards() reports its own errors, so we don't do so here + + return false; + } + + if (!RewritePersistentAllocs(bb)) { + LLDB_LOG(log, "RewritePersistentAllocs() failed"); + + // RewritePersistentAllocs() reports its own errors, so we don't do so + // here + + return false; + } + + if (!RemoveCXAAtExit(bb)) { + LLDB_LOG(log, "RemoveCXAAtExit() failed"); + + // RemoveCXAAtExit() reports its own errors, so we don't do so here + + return false; + } + } + } + + /////////////////////////////////////////////////////////////////////////////// + // Fix all Objective-C constant strings to use NSStringWithCString:encoding: + // + + if (!RewriteObjCConstStrings()) { + LLDB_LOG(log, "RewriteObjCConstStrings() failed"); + + // RewriteObjCConstStrings() reports its own errors, so we don't do so here + + return false; + } + + for (llvm::Function &function : *m_module) { + for (llvm::BasicBlock &bb : function) { + if (!RewriteObjCSelectors(bb)) { + LLDB_LOG(log, "RewriteObjCSelectors() failed"); + + // RewriteObjCSelectors() reports its own errors, so we don't do so + // here + + return false; + } + + if (!RewriteObjCClassReferences(bb)) { + LLDB_LOG(log, "RewriteObjCClassReferences() failed"); + + // RewriteObjCClasses() reports its own errors, so we don't do so here + + return false; + } + } + } + + for (llvm::Function &function : *m_module) { + for (BasicBlock &bb : function) { + if (!ResolveCalls(bb)) { + LLDB_LOG(log, "ResolveCalls() failed"); + + // ResolveCalls() reports its own errors, so we don't do so here + + return false; + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Run function-level passes that only make sense on the main function + // + + if (main_function) { + if (!ResolveExternals(*main_function)) { + LLDB_LOG(log, "ResolveExternals() failed"); + + // ResolveExternals() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceVariables(*main_function)) { + LLDB_LOG(log, "ReplaceVariables() failed"); + + // ReplaceVariables() reports its own errors, so we don't do so here + + return false; + } + } + + if (log && log->GetVerbose()) { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, nullptr); + + oss.flush(); + + LLDB_LOG(log, "Module after preparing for execution: \n\"{0}\"", s); + } + + return true; +} + +void IRForTarget::assignPassManager(PMStack &pass_mgr_stack, + PassManagerType pass_mgr_type) {} + +PassManagerType IRForTarget::getPotentialPassManagerType() const { + return PMT_ModulePassManager; +} diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h new file mode 100644 index 00000000000..262e8ee0c06 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h @@ -0,0 +1,509 @@ +//===-- IRForTarget.h ---------------------------------------------*- C++ +//-*-===// +// +// 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 liblldb_IRForTarget_h_ +#define liblldb_IRForTarget_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/lldb-public.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/Pass.h" + +#include <functional> +#include <map> + +namespace llvm { +class BasicBlock; +class CallInst; +class Constant; +class ConstantInt; +class Function; +class GlobalValue; +class GlobalVariable; +class Instruction; +class Module; +class StoreInst; +class DataLayout; +class Value; +} + +namespace lldb_private { +class ClangExpressionDeclMap; +class IRExecutionUnit; +class IRMemoryMap; +} + +/// \class IRForTarget IRForTarget.h "lldb/Expression/IRForTarget.h" +/// Transforms the IR for a function to run in the target +/// +/// Once an expression has been parsed and converted to IR, it can run in two +/// contexts: interpreted by LLDB as a DWARF location expression, or compiled +/// by the JIT and inserted into the target process for execution. +/// +/// IRForTarget makes the second possible, by applying a series of +/// transformations to the IR which make it relocatable. These +/// transformations are discussed in more detail next to their relevant +/// functions. +class IRForTarget : public llvm::ModulePass { +public: + enum class LookupResult { Success, Fail, Ignore }; + + /// Constructor + /// + /// \param[in] decl_map + /// The list of externally-referenced variables for the expression, + /// for use in looking up globals and allocating the argument + /// struct. See the documentation for ClangExpressionDeclMap. + /// + /// \param[in] resolve_vars + /// True if the external variable references (including persistent + /// variables) should be resolved. If not, only external functions + /// are resolved. + /// + /// \param[in] execution_unit + /// The holder for raw data associated with the expression. + /// + /// \param[in] error_stream + /// If non-NULL, a stream on which errors can be printed. + /// + /// \param[in] func_name + /// The name of the function to prepare for execution in the target. + IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, bool resolve_vars, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Stream &error_stream, + const char *func_name = "$__lldb_expr"); + + /// Destructor + ~IRForTarget() override; + + /// Run this IR transformer on a single module + /// + /// Implementation of the llvm::ModulePass::runOnModule() function. + /// + /// \param[in] llvm_module + /// The module to run on. This module is searched for the function + /// $__lldb_expr, and that function is passed to the passes one by + /// one. + /// + /// \return + /// True on success; false otherwise + bool runOnModule(llvm::Module &llvm_module) override; + + /// Interface stub + /// + /// Implementation of the llvm::ModulePass::assignPassManager() function. + void assignPassManager(llvm::PMStack &pass_mgr_stack, + llvm::PassManagerType pass_mgr_type = + llvm::PMT_ModulePassManager) override; + + /// Returns PMT_ModulePassManager + /// + /// Implementation of the llvm::ModulePass::getPotentialPassManagerType() + /// function. + llvm::PassManagerType getPotentialPassManagerType() const override; + +private: + /// Ensures that the current function's linkage is set to external. + /// Otherwise the JIT may not return an address for it. + /// + /// \param[in] llvm_function + /// The function whose linkage is to be fixed. + /// + /// \return + /// True on success; false otherwise. + bool FixFunctionLinkage(llvm::Function &llvm_function); + + /// A module-level pass to replace all function pointers with their + /// integer equivalents. + + /// The top-level pass implementation + /// + /// \param[in] llvm_function + /// The function currently being processed. + /// + /// \return + /// True on success; false otherwise. + bool HasSideEffects(llvm::Function &llvm_function); + + /// A function-level pass to check whether the function has side + /// effects. + + /// Get the address of a function, and a location to put the complete Value + /// of the function if one is available. + /// + /// \param[in] function + /// The function to find the location of. + /// + /// \param[out] ptr + /// The location of the function in the target. + /// + /// \param[out] name + /// The resolved name of the function (matters for intrinsics). + /// + /// \param[out] value_ptr + /// A variable to put the function's completed Value* in, or NULL + /// if the Value* shouldn't be stored anywhere. + /// + /// \return + /// The pointer. + LookupResult GetFunctionAddress(llvm::Function *function, uint64_t &ptr, + lldb_private::ConstString &name, + llvm::Constant **&value_ptr); + + /// A function-level pass to take the generated global value + /// $__lldb_expr_result and make it into a persistent variable. Also see + /// ASTResultSynthesizer. + + /// Find the NamedDecl corresponding to a Value. This interface is exposed + /// for the IR interpreter. + /// + /// \param[in] global_val + /// The global entity to search for + /// + /// \param[in] module + /// The module containing metadata to search + /// + /// \return + /// The corresponding variable declaration +public: + static clang::NamedDecl *DeclForGlobal(const llvm::GlobalValue *global_val, + llvm::Module *module); + +private: + clang::NamedDecl *DeclForGlobal(llvm::GlobalValue *global); + + /// Set the constant result variable m_const_result to the provided + /// constant, assuming it can be evaluated. The result variable will be + /// reset to NULL later if the expression has side effects. + /// + /// \param[in] initializer + /// The constant initializer for the variable. + /// + /// \param[in] name + /// The name of the result variable. + /// + /// \param[in] type + /// The Clang type of the result variable. + void MaybeSetConstantResult(llvm::Constant *initializer, + lldb_private::ConstString name, + lldb_private::TypeFromParser type); + + /// If the IR represents a cast of a variable, set m_const_result to the + /// result of the cast. The result variable will be reset to + /// NULL latger if the expression has side effects. + /// + /// \param[in] type + /// The Clang type of the result variable. + void MaybeSetCastResult(lldb_private::TypeFromParser type); + + /// The top-level pass implementation + /// + /// \param[in] llvm_function + /// The function currently being processed. + /// + /// \return + /// True on success; false otherwise + bool CreateResultVariable(llvm::Function &llvm_function); + + /// A module-level pass to find Objective-C constant strings and + /// transform them to calls to CFStringCreateWithBytes. + + /// Rewrite a single Objective-C constant string. + /// + /// \param[in] NSStr + /// The constant NSString to be transformed + /// + /// \param[in] CStr + /// The constant C string inside the NSString. This will be + /// passed as the bytes argument to CFStringCreateWithBytes. + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCConstString(llvm::GlobalVariable *NSStr, + llvm::GlobalVariable *CStr); + + /// The top-level pass implementation + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCConstStrings(); + + /// A basic block-level pass to find all Objective-C method calls and + /// rewrite them to use sel_registerName instead of statically allocated + /// selectors. The reason is that the selectors are created on the + /// assumption that the Objective-C runtime will scan the appropriate + /// section and prepare them. This doesn't happen when code is copied into + /// the target, though, and there's no easy way to induce the runtime to + /// scan them. So instead we get our selectors from sel_registerName. + + /// Replace a single selector reference + /// + /// \param[in] selector_load + /// The load of the statically-allocated selector. + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCSelector(llvm::Instruction *selector_load); + + /// The top-level pass implementation + /// + /// \param[in] basic_block + /// The basic block currently being processed. + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCSelectors(llvm::BasicBlock &basic_block); + + /// A basic block-level pass to find all Objective-C class references that + /// use the old-style Objective-C runtime and rewrite them to use + /// class_getClass instead of statically allocated class references. + + /// Replace a single old-style class reference + /// + /// \param[in] class_load + /// The load of the statically-allocated selector. + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCClassReference(llvm::Instruction *class_load); + + /// The top-level pass implementation + /// + /// \param[in] basic_block + /// The basic block currently being processed. + /// + /// \return + /// True on success; false otherwise + bool RewriteObjCClassReferences(llvm::BasicBlock &basic_block); + + /// A basic block-level pass to find all newly-declared persistent + /// variables and register them with the ClangExprDeclMap. This allows them + /// to be materialized and dematerialized like normal external variables. + /// Before transformation, these persistent variables look like normal + /// locals, so they have an allocation. This pass excises these allocations + /// and makes references look like external references where they will be + /// resolved -- like all other external references -- by ResolveExternals(). + + /// Handle a single allocation of a persistent variable + /// + /// \param[in] persistent_alloc + /// The allocation of the persistent variable. + /// + /// \return + /// True on success; false otherwise + bool RewritePersistentAlloc(llvm::Instruction *persistent_alloc); + + /// The top-level pass implementation + /// + /// \param[in] basic_block + /// The basic block currently being processed. + bool RewritePersistentAllocs(llvm::BasicBlock &basic_block); + + /// A function-level pass to find all external variables and functions + /// used in the IR. Each found external variable is added to the struct, + /// and each external function is resolved in place, its call replaced with + /// a call to a function pointer whose value is the address of the function + /// in the target process. + + /// Handle a single externally-defined variable + /// + /// \param[in] value + /// The variable. + /// + /// \return + /// True on success; false otherwise + bool MaybeHandleVariable(llvm::Value *value); + + /// Handle a single externally-defined symbol + /// + /// \param[in] symbol + /// The symbol. + /// + /// \return + /// True on success; false otherwise + bool HandleSymbol(llvm::Value *symbol); + + /// Handle a single externally-defined Objective-C class + /// + /// \param[in] classlist_reference + /// The reference, usually "01L_OBJC_CLASSLIST_REFERENCES_$_n" + /// where n (if present) is an index. + /// + /// \return + /// True on success; false otherwise + bool HandleObjCClass(llvm::Value *classlist_reference); + + /// Handle all the arguments to a function call + /// + /// \param[in] call_inst + /// The call instruction. + /// + /// \return + /// True on success; false otherwise + bool MaybeHandleCallArguments(llvm::CallInst *call_inst); + + /// Resolve variable references in calls to external functions + /// + /// \param[in] basic_block + /// The basic block currently being processed. + /// + /// \return + /// True on success; false otherwise + bool ResolveCalls(llvm::BasicBlock &basic_block); + + /// Remove calls to __cxa_atexit, which should never be generated by + /// expressions. + /// + /// \param[in] basic_block + /// The basic block currently being processed. + /// + /// \return + /// True if the scan was successful; false if some operation + /// failed + bool RemoveCXAAtExit(llvm::BasicBlock &basic_block); + + /// The top-level pass implementation + /// + /// \param[in] llvm_function + /// The function currently being processed. + /// + /// \return + /// True on success; false otherwise + bool ResolveExternals(llvm::Function &llvm_function); + + /// A basic block-level pass to excise guard variables from the code. + /// The result for the function is passed through Clang as a static + /// variable. Static variables normally have guard variables to ensure that + /// they are only initialized once. + + /// Rewrite a load to a guard variable to return constant 0. + /// + /// \param[in] guard_load + /// The load instruction to zero out. + void TurnGuardLoadIntoZero(llvm::Instruction *guard_load); + + /// The top-level pass implementation + /// + /// \param[in] basic_block + /// The basic block currently being processed. + /// + /// \return + /// True on success; false otherwise + bool RemoveGuards(llvm::BasicBlock &basic_block); + + /// A function-level pass to make all external variable references + /// point at the correct offsets from the void* passed into the function. + /// ClangExpressionDeclMap::DoStructLayout() must be called beforehand, so + /// that the offsets are valid. + + /// The top-level pass implementation + /// + /// \param[in] llvm_function + /// The function currently being processed. + /// + /// \return + /// True on success; false otherwise + bool ReplaceVariables(llvm::Function &llvm_function); + + /// Flags + bool m_resolve_vars; ///< True if external variable references and persistent + ///variable references should be resolved + lldb_private::ConstString + m_func_name; ///< The name of the function to translate + lldb_private::ConstString + m_result_name; ///< The name of the result variable ($0, $1, ...) + lldb_private::TypeFromParser + m_result_type; ///< The type of the result variable. + llvm::Module *m_module; ///< The module being processed, or NULL if that has + ///not been determined yet. + std::unique_ptr<llvm::DataLayout> m_target_data; ///< The target data for the + ///module being processed, or + ///NULL if there is no + ///module. + lldb_private::ClangExpressionDeclMap + *m_decl_map; ///< The DeclMap containing the Decls + llvm::FunctionCallee + m_CFStringCreateWithBytes; ///< The address of the function + /// CFStringCreateWithBytes, cast to the + /// appropriate function pointer type + llvm::FunctionCallee m_sel_registerName; ///< The address of the function + /// sel_registerName, cast to the + /// appropriate function pointer type + llvm::FunctionCallee m_objc_getClass; ///< The address of the function + /// objc_getClass, cast to the + /// appropriate function pointer type + llvm::IntegerType + *m_intptr_ty; ///< The type of an integer large enough to hold a pointer. + lldb_private::Stream + &m_error_stream; ///< The stream on which errors should be printed + lldb_private::IRExecutionUnit & + m_execution_unit; ///< The execution unit containing the IR being created. + + llvm::StoreInst *m_result_store; ///< If non-NULL, the store instruction that + ///writes to the result variable. If + /// m_has_side_effects is true, this is + /// NULL. + bool m_result_is_pointer; ///< True if the function's result in the AST is a + ///pointer (see comments in + /// ASTResultSynthesizer::SynthesizeBodyResult) + + /// A placeholder that will be replaced by a pointer to the final location of + /// the static allocation. + llvm::GlobalVariable *m_reloc_placeholder; + + class FunctionValueCache { + public: + typedef std::function<llvm::Value *(llvm::Function *)> Maker; + + FunctionValueCache(Maker const &maker); + ~FunctionValueCache(); + llvm::Value *GetValue(llvm::Function *function); + + private: + Maker const m_maker; + typedef std::map<llvm::Function *, llvm::Value *> FunctionValueMap; + FunctionValueMap m_values; + }; + + FunctionValueCache m_entry_instruction_finder; + + /// UnfoldConstant operates on a constant [Old] which has just been replaced + /// with a value [New]. We assume that new_value has been properly placed + /// early in the function, in front of the first instruction in the entry + /// basic block [FirstEntryInstruction]. + /// + /// UnfoldConstant reads through the uses of Old and replaces Old in those + /// uses with New. Where those uses are constants, the function generates + /// new instructions to compute the result of the new, non-constant + /// expression and places them before FirstEntryInstruction. These + /// instructions replace the constant uses, so UnfoldConstant calls itself + /// recursively for those. + /// + /// \return + /// True on success; false otherwise + static bool UnfoldConstant(llvm::Constant *old_constant, + llvm::Function *llvm_function, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder, + lldb_private::Stream &error_stream); + + /// Commit the allocation in m_data_allocator and use its final location to + /// replace m_reloc_placeholder. + /// + /// \return + /// True on success; false otherwise + bool CompleteDataAllocation(); +}; + +#endif // liblldb_IRForTarget_h_ diff --git a/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h new file mode 100644 index 00000000000..7553860f249 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h @@ -0,0 +1,40 @@ +//===-- ModuleDependencyCollector.h -----------------------------*- C++ -*-===// +// +// 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 liblldb_ModuleDependencyCollector_h_ +#define liblldb_ModuleDependencyCollector_h_ + +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileCollector.h" + +namespace lldb_private { +class ModuleDependencyCollectorAdaptor + : public clang::ModuleDependencyCollector { +public: + ModuleDependencyCollectorAdaptor( + std::shared_ptr<llvm::FileCollector> file_collector) + : clang::ModuleDependencyCollector(""), m_file_collector(file_collector) { + } + + void addFile(llvm::StringRef Filename, + llvm::StringRef FileDst = {}) override { + if (m_file_collector) + m_file_collector->addFile(Filename); + } + + bool insertSeen(llvm::StringRef Filename) override { return false; } + void addFileMapping(llvm::StringRef VPath, llvm::StringRef RPath) override {} + void writeFileMap() override {} + +private: + std::shared_ptr<llvm::FileCollector> m_file_collector; +}; +} // namespace lldb_private + +#endif |