diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp new file mode 100644 index 000000000000..35038a56440d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -0,0 +1,1002 @@ +//===-- ClangUserExpression.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 <cstdio> +#include <sys/types.h> + +#include <cstdlib> +#include <map> +#include <string> + +#include "ClangUserExpression.h" + +#include "ASTResultSynthesizer.h" +#include "ClangASTMetadata.h" +#include "ClangDiagnostic.h" +#include "ClangExpressionDeclMap.h" +#include "ClangExpressionParser.h" +#include "ClangModulesDeclVendor.h" +#include "ClangPersistentVariables.h" +#include "CppModuleConfiguration.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.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/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/LLDBLog.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" +#include "llvm/BinaryFormat/Dwarf.h" + +using namespace lldb_private; + +char ClangUserExpression::ID; + +ClangUserExpression::ClangUserExpression( + ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, SourceLanguage 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.name) { + case llvm::dwarf::DW_LNAME_C_plus_plus: + m_allow_cxx = true; + break; + case llvm::dwarf::DW_LNAME_ObjC: + m_allow_objc = true; + break; + case llvm::dwarf::DW_LNAME_ObjC_plus_plus: + default: + m_allow_cxx = true; + m_allow_objc = true; + break; + } +} + +ClangUserExpression::~ClangUserExpression() = default; + +void ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) { + Log *log = GetLog(LLDBLog::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 = + TypeSystemClang::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 = + TypeSystemClang::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 = + TypeSystemClang::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 = + TypeSystemClang::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 (TypeSystemClang::IsObjCClassType(self_clang_type)) { + return; + } else if (TypeSystemClang::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( + lldb::eSeverityError, "couldn't start parsing (no persistent data)"); + return false; + } + } else { + diagnostic_manager.PutString(lldb::eSeverityError, + "error: couldn't start parsing (no target)"); + return false; + } + return true; +} + +static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target, + DiagnosticManager &diagnostic_manager) { + if (!target->GetEnableAutoImportClangModules()) + return; + + auto *persistent_state = llvm::cast<ClangPersistentVariables>( + target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); + if (!persistent_state) + return; + + std::shared_ptr<ClangModulesDeclVendor> decl_vendor = + persistent_state->GetClangModulesDeclVendor(); + if (!decl_vendor) + return; + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) + return; + + Block *block = frame->GetFrameBlock(); + if (!block) + return; + SymbolContext sc; + + block->CalculateSymbolContext(&sc); + + if (!sc.comp_unit) + return; + StreamString error_stream; + + ClangModulesDeclVendor::ModuleVector modules_for_macros = + persistent_state->GetHandLoadedClangModules(); + if (decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros, + error_stream)) + return; + + // Failed to load some modules, so emit the error stream as a diagnostic. + if (!error_stream.Empty()) { + // The error stream already contains several Clang diagnostics that might + // be either errors or warnings, so just print them all as one remark + // diagnostic to prevent that the message starts with "error: error:". + diagnostic_manager.PutString(lldb::eSeverityInfo, error_stream.GetString()); + return; + } + + diagnostic_manager.PutString(lldb::eSeverityError, + "Unknown error while loading modules needed for " + "current compilation unit."); +} + +ClangExpressionSourceCode::WrapKind ClangUserExpression::GetWrapKind() const { + assert(m_options.GetExecutionPolicy() != eExecutionPolicyTopLevel && + "Top level expressions aren't wrapped."); + using Kind = ClangExpressionSourceCode::WrapKind; + if (m_in_cplusplus_method) + return Kind::CppMemberFunction; + else if (m_in_objectivec_method) { + if (m_in_static_method) + return Kind::ObjCStaticMethod; + return Kind::ObjCInstanceMethod; + } + // Not in any kind of 'special' function, so just wrap it in a normal C + // function. + return Kind::Function; +} + +void ClangUserExpression::CreateSourceCode( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + std::vector<std::string> modules_to_import, bool for_completion) { + + 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, m_expr_text, GetWrapKind())); + + if (!m_source_code->GetText(m_transformed_text, exe_ctx, !m_ctx_obj, + for_completion, modules_to_import)) { + diagnostic_manager.PutString(lldb::eSeverityError, + "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, 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 = GetLog(LLDBLog::Expressions); + LLDB_LOG(log, "[C++ module config] {0}", msg); + return CppModuleConfiguration(); +} + +CppModuleConfiguration GetModuleConfig(lldb::LanguageType language, + ExecutionContext &exe_ctx) { + Log *log = GetLog(LLDBLog::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"); + + 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 (auto &f : sc.comp_unit->GetSupportFiles()) + files.AppendIfUnique(f->Materialize()); + // 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 SupportFileList &support_files = + module.GetCompileUnitAtIndex(i)->GetSupportFiles(); + for (auto &f : support_files) { + files.AppendIfUnique(f->Materialize()); + } + } + return false; + }); + + LLDB_LOG(log, "[C++ module config] Found {0} support files to analyze", + files.GetSize()); + if (log && log->GetVerbose()) { + for (auto &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, target->GetArchitecture().GetTriple()); +} + +bool ClangUserExpression::PrepareForParsing( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + bool for_completion) { + InstallContext(exe_ctx); + + if (!SetupPersistentState(diagnostic_manager, exe_ctx)) + return false; + + Status err; + ScanContext(exe_ctx, err); + + if (!err.Success()) { + diagnostic_manager.PutString(lldb::eSeverityWarning, err.AsCString()); + } + + //////////////////////////////////// + // Generate the expression + // + + ApplyObjcCastHack(m_expr_text); + + SetupDeclVendor(exe_ctx, m_target, diagnostic_manager); + + m_filename = m_clang_state->GetNextExprFileName(); + + if (m_target->GetImportStdModule() == eImportStdModuleTrue) + SetupCppModuleImports(exe_ctx); + + CreateSourceCode(diagnostic_manager, exe_ctx, m_imported_cpp_modules, + for_completion); + return true; +} + +bool ClangUserExpression::TryParse( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, bool keep_result_in_memory, + bool generate_debug_info) { + m_materializer_up = std::make_unique<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( + lldb::eSeverityError, + "current process state is unsuitable for expression parsing"); + return false; + } + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + DeclMap()->SetLookupsEnabled(true); + } + + m_parser = std::make_unique<ClangExpressionParser>( + exe_ctx.GetBestExecutionContextScope(), *this, generate_debug_info, + m_include_directories, m_filename); + + unsigned num_errors = m_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 (m_parser->RewriteExpression(diagnostic_manager)) { + size_t fixed_start; + size_t fixed_end; + m_fixed_text = 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( + m_fixed_text, fixed_start, fixed_end)) + m_fixed_text = + m_fixed_text.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 = m_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(lldb::eSeverityError, error_cstr); + else + diagnostic_manager.PutString(lldb::eSeverityError, + "expression can't be interpreted or run"); + return false; + } + } + return true; +} + +void ClangUserExpression::SetupCppModuleImports(ExecutionContext &exe_ctx) { + Log *log = GetLog(LLDBLog::Expressions); + + CppModuleConfiguration module_config = + GetModuleConfig(m_language.AsLanguageType(), exe_ctx); + m_imported_cpp_modules = module_config.GetImportedModules(); + m_include_directories = module_config.GetIncludeDirs(); + + LLDB_LOG(log, "List of imported modules in expression: {0}", + llvm::make_range(m_imported_cpp_modules.begin(), + m_imported_cpp_modules.end())); + LLDB_LOG(log, "List of include directories gathered for modules: {0}", + llvm::make_range(m_include_directories.begin(), + m_include_directories.end())); +} + +static bool shouldRetryWithCppModule(Target &target, ExecutionPolicy exe_policy) { + // Top-level expression don't yet support importing C++ modules. + if (exe_policy == ExecutionPolicy::eExecutionPolicyTopLevel) + return false; + return target.GetImportStdModule() == eImportStdModuleFallback; +} + +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 = GetLog(LLDBLog::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(lldb::eSeverityError, "invalid target"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + bool parse_success = TryParse(diagnostic_manager, exe_ctx, execution_policy, + keep_result_in_memory, generate_debug_info); + // If the expression failed to parse, check if retrying parsing with a loaded + // C++ module is possible. + if (!parse_success && shouldRetryWithCppModule(*target, execution_policy)) { + // Load the loaded C++ modules. + SetupCppModuleImports(exe_ctx); + // If we did load any modules, then retry parsing. + if (!m_imported_cpp_modules.empty()) { + // Create a dedicated diagnostic manager for the second parse attempt. + // These diagnostics are only returned to the caller if using the fallback + // actually succeeded in getting the expression to parse. This prevents + // that module-specific issues regress diagnostic quality with the + // fallback mode. + DiagnosticManager retry_manager; + // The module imports are injected into the source code wrapper, + // so recreate those. + CreateSourceCode(retry_manager, exe_ctx, m_imported_cpp_modules, + /*for_completion*/ false); + parse_success = TryParse(retry_manager, exe_ctx, execution_policy, + keep_result_in_memory, generate_debug_info); + // Return the parse diagnostics if we were successful. + if (parse_success) + diagnostic_manager = std::move(retry_manager); + } + } + if (!parse_success) + 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.AsLanguageType())) + 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.SetFilename(const_func_name); + jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString()); + m_jit_module_wp = jit_module_sp; + target->GetImages().Append(jit_module_sp); + } + } + + Process *process = exe_ctx.GetProcessPtr(); + 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 = GetLog(LLDBLog::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 = std::make_unique<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( + lldb::eSeverityError, + "current process state is unsuitable for expression parsing"); + + return false; + } + + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + DeclMap()->SetLookupsEnabled(true); + } + + ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *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) + 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; +} + +lldb::addr_t ClangUserExpression::GetCppObjectPointer( + lldb::StackFrameSP frame_sp, llvm::StringRef object_name, Status &err) { + auto valobj_sp = + GetObjectPointerValueObject(std::move(frame_sp), object_name, err); + + // We're inside a C++ class method. This could potentially be an unnamed + // lambda structure. If the lambda captured a "this", that should be + // the object pointer. + if (auto thisChildSP = valobj_sp->GetChildMemberWithName("this")) { + valobj_sp = thisChildSP; + } + + if (!err.Success() || !valobj_sp.get()) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (ret == LLDB_INVALID_ADDRESS) { + err.SetErrorStringWithFormatv( + "Couldn't load '{0}' because its value couldn't be evaluated", + object_name); + return LLDB_INVALID_ADDRESS; + } + + return ret; +} + +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; + + if (!m_in_cplusplus_method && !m_in_objectivec_method) { + diagnostic_manager.PutString( + lldb::eSeverityError, + "need object pointer but don't know the language"); + return false; + } + + static constexpr llvm::StringLiteral g_cplusplus_object_name("this"); + static constexpr llvm::StringLiteral g_objc_object_name("self"); + llvm::StringRef object_name = + m_in_cplusplus_method ? g_cplusplus_object_name : g_objc_object_name; + + 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 { + if (m_in_cplusplus_method) { + object_ptr = + GetCppObjectPointer(frame_sp, object_name, object_ptr_error); + } else { + object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error); + } + } + + if (!object_ptr_error.Success()) { + exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Format( + "warning: `{0}' is not accessible (substituting 0). {1}\n", + object_name, object_ptr_error.AsCString()); + object_ptr = 0; + } + + if (m_in_objectivec_method) { + static constexpr llvm::StringLiteral cmd_name("_cmd"); + + cmd_ptr = GetObjectPointer(frame_sp, cmd_name, object_ptr_error); + + if (!object_ptr_error.Success()) { + diagnostic_manager.Printf( + lldb::eSeverityWarning, + "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(); +} + +char ClangUserExpression::ClangUserExpressionHelper::ID; + +void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap( + ExecutionContext &exe_ctx, + Materializer::PersistentVariableDelegate &delegate, + bool keep_result_in_memory, + ValueObject *ctx_obj) { + std::shared_ptr<ClangASTImporter> ast_importer; + auto *state = exe_ctx.GetTargetSP()->GetPersistentExpressionStateForLanguage( + lldb::eLanguageTypeC); + if (state) { + auto *persistent_vars = llvm::cast<ClangPersistentVariables>(state); + ast_importer = persistent_vars->GetClangASTImporter(); + } + m_expr_decl_map_up = std::make_unique<ClangExpressionDeclMap>( + keep_result_in_memory, &delegate, exe_ctx.GetTargetSP(), ast_importer, + ctx_obj); +} + +clang::ASTConsumer * +ClangUserExpression::ClangUserExpressionHelper::ASTTransformer( + clang::ASTConsumer *passthrough) { + m_result_synthesizer_up = std::make_unique<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() { + return m_persistent_state->GetNextPersistentVariableName(false); +} + +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; +} |