Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.cpp
39587 views
//===-- CommandObjectDWIMPrint.cpp ------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "CommandObjectDWIMPrint.h"910#include "lldb/Core/ValueObject.h"11#include "lldb/DataFormatters/DumpValueObjectOptions.h"12#include "lldb/Expression/ExpressionVariable.h"13#include "lldb/Expression/UserExpression.h"14#include "lldb/Interpreter/CommandInterpreter.h"15#include "lldb/Interpreter/CommandObject.h"16#include "lldb/Interpreter/CommandReturnObject.h"17#include "lldb/Interpreter/OptionGroupFormat.h"18#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"19#include "lldb/Target/StackFrame.h"20#include "lldb/Utility/ConstString.h"21#include "lldb/lldb-defines.h"22#include "lldb/lldb-enumerations.h"23#include "lldb/lldb-forward.h"24#include "llvm/ADT/StringRef.h"2526#include <regex>2728using namespace llvm;29using namespace lldb;30using namespace lldb_private;3132CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)33: CommandObjectRaw(interpreter, "dwim-print",34"Print a variable or expression.",35"dwim-print [<variable-name> | <expression>]",36eCommandProcessMustBePaused | eCommandTryTargetAPILock) {3738AddSimpleArgumentList(eArgTypeVarName);3940m_option_group.Append(&m_format_options,41OptionGroupFormat::OPTION_GROUP_FORMAT |42OptionGroupFormat::OPTION_GROUP_GDB_FMT,43LLDB_OPT_SET_1);44StringRef exclude_expr_options[] = {"debug", "top-level"};45m_option_group.Append(&m_expr_options, exclude_expr_options);46m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);47m_option_group.Finalize();48}4950Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }5152void CommandObjectDWIMPrint::DoExecute(StringRef command,53CommandReturnObject &result) {54m_option_group.NotifyOptionParsingStarting(&m_exe_ctx);55OptionsWithRaw args{command};56StringRef expr = args.GetRawPart();5758if (expr.empty()) {59result.AppendErrorWithFormatv("'{0}' takes a variable or expression",60m_cmd_name);61return;62}6364if (args.HasArgs()) {65if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,66m_exe_ctx))67return;68}6970// If the user has not specified, default to disabling persistent results.71if (m_expr_options.suppress_persistent_result == eLazyBoolCalculate)72m_expr_options.suppress_persistent_result = eLazyBoolYes;73bool suppress_result = m_expr_options.ShouldSuppressResult(m_varobj_options);7475auto verbosity = GetDebugger().GetDWIMPrintVerbosity();7677Target *target_ptr = m_exe_ctx.GetTargetPtr();78// Fallback to the dummy target, which can allow for expression evaluation.79Target &target = target_ptr ? *target_ptr : GetDummyTarget();8081EvaluateExpressionOptions eval_options =82m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options);83// This command manually removes the result variable, make sure expression84// evaluation doesn't do it first.85eval_options.SetSuppressPersistentResult(false);8687DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions(88m_expr_options.m_verbosity, m_format_options.GetFormat());89dump_options.SetHideRootName(suppress_result);9091bool is_po = m_varobj_options.use_objc;9293StackFrame *frame = m_exe_ctx.GetFramePtr();9495// Either the language was explicitly specified, or we check the frame.96lldb::LanguageType language = m_expr_options.language;97if (language == lldb::eLanguageTypeUnknown && frame)98language = frame->GuessLanguage().AsLanguageType();99100// Add a hint if object description was requested, but no description101// function was implemented.102auto maybe_add_hint = [&](llvm::StringRef output) {103// Identify the default output of object description for Swift and104// Objective-C105// "<Name: 0x...>. The regex is:106// - Start with "<".107// - Followed by 1 or more non-whitespace characters.108// - Followed by ": 0x".109// - Followed by 5 or more hex digits.110// - Followed by ">".111// - End with zero or more whitespace characters.112const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");113114if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&115(language == lldb::eLanguageTypeSwift ||116language == lldb::eLanguageTypeObjC) &&117std::regex_match(output.data(), swift_class_regex)) {118119static bool note_shown = false;120if (note_shown)121return;122123result.GetOutputStream()124<< "note: object description requested, but type doesn't implement "125"a custom object description. Consider using \"p\" instead of "126"\"po\" (this note will only be shown once per debug session).\n";127note_shown = true;128}129};130131// Dump `valobj` according to whether `po` was requested or not.132auto dump_val_object = [&](ValueObject &valobj) {133if (is_po) {134StreamString temp_result_stream;135if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) {136result.AppendError(toString(std::move(error)));137return;138}139llvm::StringRef output = temp_result_stream.GetString();140maybe_add_hint(output);141result.GetOutputStream() << output;142} else {143llvm::Error error =144valobj.Dump(result.GetOutputStream(), dump_options);145if (error) {146result.AppendError(toString(std::move(error)));147return;148}149}150result.SetStatus(eReturnStatusSuccessFinishResult);151};152153// First, try `expr` as the name of a frame variable.154if (frame) {155auto valobj_sp = frame->FindVariable(ConstString(expr));156if (valobj_sp && valobj_sp->GetError().Success()) {157if (!suppress_result) {158if (auto persisted_valobj = valobj_sp->Persist())159valobj_sp = persisted_valobj;160}161162if (verbosity == eDWIMPrintVerbosityFull) {163StringRef flags;164if (args.HasArgs())165flags = args.GetArgString();166result.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`",167flags, expr);168}169170dump_val_object(*valobj_sp);171return;172}173}174175// Second, try `expr` as a persistent variable.176if (expr.starts_with("$"))177if (auto *state = target.GetPersistentExpressionStateForLanguage(language))178if (auto var_sp = state->GetVariable(expr))179if (auto valobj_sp = var_sp->GetValueObject()) {180dump_val_object(*valobj_sp);181return;182}183184// Third, and lastly, try `expr` as a source expression to evaluate.185{186auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();187ValueObjectSP valobj_sp;188std::string fixed_expression;189190ExpressionResults expr_result = target.EvaluateExpression(191expr, exe_scope, valobj_sp, eval_options, &fixed_expression);192193// Only mention Fix-Its if the expression evaluator applied them.194// Compiler errors refer to the final expression after applying Fix-It(s).195if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {196Stream &error_stream = result.GetErrorStream();197error_stream << " Evaluated this expression after applying Fix-It(s):\n";198error_stream << " " << fixed_expression << "\n";199}200201// If the expression failed, return an error.202if (expr_result != eExpressionCompleted) {203if (valobj_sp)204result.SetError(valobj_sp->GetError());205else206result.AppendErrorWithFormatv(207"unknown error evaluating expression `{0}`", expr);208return;209}210211if (verbosity != eDWIMPrintVerbosityNone) {212StringRef flags;213if (args.HasArgs())214flags = args.GetArgStringWithDelimiter();215result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags,216expr);217}218219if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)220dump_val_object(*valobj_sp);221else222result.SetStatus(eReturnStatusSuccessFinishResult);223224if (suppress_result)225if (auto result_var_sp =226target.GetPersistentVariable(valobj_sp->GetName())) {227auto language = valobj_sp->GetPreferredDisplayLanguage();228if (auto *persistent_state =229target.GetPersistentExpressionStateForLanguage(language))230persistent_state->RemovePersistentVariable(result_var_sp);231}232}233}234235236