1bdd1243dSDimitry Andric //===-- CommandObjectDWIMPrint.cpp ------------------------------*- C++ -*-===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "CommandObjectDWIMPrint.h" 10bdd1243dSDimitry Andric 11bdd1243dSDimitry Andric #include "lldb/Core/ValueObject.h" 1206c3fb27SDimitry Andric #include "lldb/DataFormatters/DumpValueObjectOptions.h" 1306c3fb27SDimitry Andric #include "lldb/Expression/ExpressionVariable.h" 1406c3fb27SDimitry Andric #include "lldb/Expression/UserExpression.h" 15bdd1243dSDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h" 16bdd1243dSDimitry Andric #include "lldb/Interpreter/CommandObject.h" 17bdd1243dSDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h" 1806c3fb27SDimitry Andric #include "lldb/Interpreter/OptionGroupFormat.h" 1906c3fb27SDimitry Andric #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" 20bdd1243dSDimitry Andric #include "lldb/Target/StackFrame.h" 21bdd1243dSDimitry Andric #include "lldb/Utility/ConstString.h" 2206c3fb27SDimitry Andric #include "lldb/lldb-defines.h" 23bdd1243dSDimitry Andric #include "lldb/lldb-enumerations.h" 24bdd1243dSDimitry Andric #include "lldb/lldb-forward.h" 2506c3fb27SDimitry Andric #include "llvm/ADT/StringRef.h" 2606c3fb27SDimitry Andric #include "llvm/Support/FormatVariadic.h" 27bdd1243dSDimitry Andric 28*5f757f3fSDimitry Andric #include <regex> 29*5f757f3fSDimitry Andric 30bdd1243dSDimitry Andric using namespace llvm; 31bdd1243dSDimitry Andric using namespace lldb; 32bdd1243dSDimitry Andric using namespace lldb_private; 33bdd1243dSDimitry Andric 34bdd1243dSDimitry Andric CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter) 35bdd1243dSDimitry Andric : CommandObjectRaw(interpreter, "dwim-print", 36bdd1243dSDimitry Andric "Print a variable or expression.", 37bdd1243dSDimitry Andric "dwim-print [<variable-name> | <expression>]", 38bdd1243dSDimitry Andric eCommandProcessMustBePaused | eCommandTryTargetAPILock) { 3906c3fb27SDimitry Andric 4006c3fb27SDimitry Andric CommandArgumentData var_name_arg(eArgTypeVarName, eArgRepeatPlain); 4106c3fb27SDimitry Andric m_arguments.push_back({var_name_arg}); 4206c3fb27SDimitry Andric 4306c3fb27SDimitry Andric m_option_group.Append(&m_format_options, 4406c3fb27SDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT | 4506c3fb27SDimitry Andric OptionGroupFormat::OPTION_GROUP_GDB_FMT, 4606c3fb27SDimitry Andric LLDB_OPT_SET_1); 4706c3fb27SDimitry Andric StringRef exclude_expr_options[] = {"debug", "top-level"}; 4806c3fb27SDimitry Andric m_option_group.Append(&m_expr_options, exclude_expr_options); 4906c3fb27SDimitry Andric m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 5006c3fb27SDimitry Andric m_option_group.Finalize(); 51bdd1243dSDimitry Andric } 52bdd1243dSDimitry Andric 5306c3fb27SDimitry Andric Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; } 5406c3fb27SDimitry Andric 5506c3fb27SDimitry Andric void CommandObjectDWIMPrint::HandleArgumentCompletion( 5606c3fb27SDimitry Andric CompletionRequest &request, OptionElementVector &opt_element_vector) { 5706c3fb27SDimitry Andric lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 5806c3fb27SDimitry Andric GetCommandInterpreter(), lldb::eVariablePathCompletion, request, nullptr); 5906c3fb27SDimitry Andric } 6006c3fb27SDimitry Andric 61*5f757f3fSDimitry Andric void CommandObjectDWIMPrint::DoExecute(StringRef command, 62bdd1243dSDimitry Andric CommandReturnObject &result) { 6306c3fb27SDimitry Andric m_option_group.NotifyOptionParsingStarting(&m_exe_ctx); 6406c3fb27SDimitry Andric OptionsWithRaw args{command}; 6506c3fb27SDimitry Andric StringRef expr = args.GetRawPart(); 66bdd1243dSDimitry Andric 67bdd1243dSDimitry Andric if (expr.empty()) { 68bdd1243dSDimitry Andric result.AppendErrorWithFormatv("'{0}' takes a variable or expression", 69bdd1243dSDimitry Andric m_cmd_name); 70*5f757f3fSDimitry Andric return; 71bdd1243dSDimitry Andric } 72bdd1243dSDimitry Andric 7306c3fb27SDimitry Andric if (args.HasArgs()) { 7406c3fb27SDimitry Andric if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, 7506c3fb27SDimitry Andric m_exe_ctx)) 76*5f757f3fSDimitry Andric return; 7706c3fb27SDimitry Andric } 7806c3fb27SDimitry Andric 7906c3fb27SDimitry Andric // If the user has not specified, default to disabling persistent results. 8006c3fb27SDimitry Andric if (m_expr_options.suppress_persistent_result == eLazyBoolCalculate) 8106c3fb27SDimitry Andric m_expr_options.suppress_persistent_result = eLazyBoolYes; 8206c3fb27SDimitry Andric bool suppress_result = m_expr_options.ShouldSuppressResult(m_varobj_options); 8306c3fb27SDimitry Andric 84bdd1243dSDimitry Andric auto verbosity = GetDebugger().GetDWIMPrintVerbosity(); 85bdd1243dSDimitry Andric 8606c3fb27SDimitry Andric Target *target_ptr = m_exe_ctx.GetTargetPtr(); 8706c3fb27SDimitry Andric // Fallback to the dummy target, which can allow for expression evaluation. 8806c3fb27SDimitry Andric Target &target = target_ptr ? *target_ptr : GetDummyTarget(); 8906c3fb27SDimitry Andric 9006c3fb27SDimitry Andric EvaluateExpressionOptions eval_options = 9106c3fb27SDimitry Andric m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options); 9206c3fb27SDimitry Andric // This command manually removes the result variable, make sure expression 9306c3fb27SDimitry Andric // evaluation doesn't do it first. 9406c3fb27SDimitry Andric eval_options.SetSuppressPersistentResult(false); 9506c3fb27SDimitry Andric 9606c3fb27SDimitry Andric DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions( 9706c3fb27SDimitry Andric m_expr_options.m_verbosity, m_format_options.GetFormat()); 9806c3fb27SDimitry Andric dump_options.SetHideRootName(suppress_result); 9906c3fb27SDimitry Andric 100*5f757f3fSDimitry Andric bool is_po = m_varobj_options.use_objc; 101*5f757f3fSDimitry Andric 10206c3fb27SDimitry Andric StackFrame *frame = m_exe_ctx.GetFramePtr(); 10306c3fb27SDimitry Andric 104*5f757f3fSDimitry Andric // Either Swift was explicitly specified, or the frame is Swift. 105*5f757f3fSDimitry Andric lldb::LanguageType language = m_expr_options.language; 106*5f757f3fSDimitry Andric if (language == lldb::eLanguageTypeUnknown && frame) 107*5f757f3fSDimitry Andric language = frame->GuessLanguage(); 108*5f757f3fSDimitry Andric 109*5f757f3fSDimitry Andric // Add a hint if object description was requested, but no description 110*5f757f3fSDimitry Andric // function was implemented. 111*5f757f3fSDimitry Andric auto maybe_add_hint = [&](llvm::StringRef output) { 112*5f757f3fSDimitry Andric // Identify the default output of object description for Swift and 113*5f757f3fSDimitry Andric // Objective-C 114*5f757f3fSDimitry Andric // "<Name: 0x...>. The regex is: 115*5f757f3fSDimitry Andric // - Start with "<". 116*5f757f3fSDimitry Andric // - Followed by 1 or more non-whitespace characters. 117*5f757f3fSDimitry Andric // - Followed by ": 0x". 118*5f757f3fSDimitry Andric // - Followed by 5 or more hex digits. 119*5f757f3fSDimitry Andric // - Followed by ">". 120*5f757f3fSDimitry Andric // - End with zero or more whitespace characters. 121*5f757f3fSDimitry Andric const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$"); 122*5f757f3fSDimitry Andric 123*5f757f3fSDimitry Andric if (GetDebugger().GetShowDontUsePoHint() && target_ptr && 124*5f757f3fSDimitry Andric (language == lldb::eLanguageTypeSwift || 125*5f757f3fSDimitry Andric language == lldb::eLanguageTypeObjC) && 126*5f757f3fSDimitry Andric std::regex_match(output.data(), swift_class_regex)) { 127*5f757f3fSDimitry Andric 128*5f757f3fSDimitry Andric static bool note_shown = false; 129*5f757f3fSDimitry Andric if (note_shown) 130*5f757f3fSDimitry Andric return; 131*5f757f3fSDimitry Andric 132*5f757f3fSDimitry Andric result.GetOutputStream() 133*5f757f3fSDimitry Andric << "note: object description requested, but type doesn't implement " 134*5f757f3fSDimitry Andric "a custom object description. Consider using \"p\" instead of " 135*5f757f3fSDimitry Andric "\"po\" (this note will only be shown once per debug session).\n"; 136*5f757f3fSDimitry Andric note_shown = true; 137*5f757f3fSDimitry Andric } 138*5f757f3fSDimitry Andric }; 139*5f757f3fSDimitry Andric 140bdd1243dSDimitry Andric // First, try `expr` as the name of a frame variable. 14106c3fb27SDimitry Andric if (frame) { 142bdd1243dSDimitry Andric auto valobj_sp = frame->FindVariable(ConstString(expr)); 143bdd1243dSDimitry Andric if (valobj_sp && valobj_sp->GetError().Success()) { 14406c3fb27SDimitry Andric if (!suppress_result) { 14506c3fb27SDimitry Andric if (auto persisted_valobj = valobj_sp->Persist()) 14606c3fb27SDimitry Andric valobj_sp = persisted_valobj; 14706c3fb27SDimitry Andric } 14806c3fb27SDimitry Andric 14906c3fb27SDimitry Andric if (verbosity == eDWIMPrintVerbosityFull) { 15006c3fb27SDimitry Andric StringRef flags; 15106c3fb27SDimitry Andric if (args.HasArgs()) 15206c3fb27SDimitry Andric flags = args.GetArgString(); 15306c3fb27SDimitry Andric result.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`", 15406c3fb27SDimitry Andric flags, expr); 15506c3fb27SDimitry Andric } 15606c3fb27SDimitry Andric 157*5f757f3fSDimitry Andric if (is_po) { 158*5f757f3fSDimitry Andric StreamString temp_result_stream; 159*5f757f3fSDimitry Andric valobj_sp->Dump(temp_result_stream, dump_options); 160*5f757f3fSDimitry Andric llvm::StringRef output = temp_result_stream.GetString(); 161*5f757f3fSDimitry Andric maybe_add_hint(output); 162*5f757f3fSDimitry Andric result.GetOutputStream() << output; 163*5f757f3fSDimitry Andric } else { 16406c3fb27SDimitry Andric valobj_sp->Dump(result.GetOutputStream(), dump_options); 165*5f757f3fSDimitry Andric } 166bdd1243dSDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 167*5f757f3fSDimitry Andric return; 168bdd1243dSDimitry Andric } 169bdd1243dSDimitry Andric } 170bdd1243dSDimitry Andric 171bdd1243dSDimitry Andric // Second, also lastly, try `expr` as a source expression to evaluate. 172bdd1243dSDimitry Andric { 173bdd1243dSDimitry Andric auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); 174bdd1243dSDimitry Andric ValueObjectSP valobj_sp; 175*5f757f3fSDimitry Andric std::string fixed_expression; 176*5f757f3fSDimitry Andric 177*5f757f3fSDimitry Andric ExpressionResults expr_result = target.EvaluateExpression( 178*5f757f3fSDimitry Andric expr, exe_scope, valobj_sp, eval_options, &fixed_expression); 179*5f757f3fSDimitry Andric 180*5f757f3fSDimitry Andric // Only mention Fix-Its if the expression evaluator applied them. 181*5f757f3fSDimitry Andric // Compiler errors refer to the final expression after applying Fix-It(s). 182*5f757f3fSDimitry Andric if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) { 183*5f757f3fSDimitry Andric Stream &error_stream = result.GetErrorStream(); 184*5f757f3fSDimitry Andric error_stream << " Evaluated this expression after applying Fix-It(s):\n"; 185*5f757f3fSDimitry Andric error_stream << " " << fixed_expression << "\n"; 186*5f757f3fSDimitry Andric } 187*5f757f3fSDimitry Andric 18806c3fb27SDimitry Andric if (expr_result == eExpressionCompleted) { 18906c3fb27SDimitry Andric if (verbosity != eDWIMPrintVerbosityNone) { 19006c3fb27SDimitry Andric StringRef flags; 19106c3fb27SDimitry Andric if (args.HasArgs()) 19206c3fb27SDimitry Andric flags = args.GetArgStringWithDelimiter(); 19306c3fb27SDimitry Andric result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags, 19406c3fb27SDimitry Andric expr); 19506c3fb27SDimitry Andric } 19606c3fb27SDimitry Andric 197*5f757f3fSDimitry Andric if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) { 198*5f757f3fSDimitry Andric if (is_po) { 199*5f757f3fSDimitry Andric StreamString temp_result_stream; 200*5f757f3fSDimitry Andric valobj_sp->Dump(temp_result_stream, dump_options); 201*5f757f3fSDimitry Andric llvm::StringRef output = temp_result_stream.GetString(); 202*5f757f3fSDimitry Andric maybe_add_hint(output); 203*5f757f3fSDimitry Andric result.GetOutputStream() << output; 204*5f757f3fSDimitry Andric } else { 20506c3fb27SDimitry Andric valobj_sp->Dump(result.GetOutputStream(), dump_options); 206*5f757f3fSDimitry Andric } 207*5f757f3fSDimitry Andric } 20806c3fb27SDimitry Andric 20906c3fb27SDimitry Andric if (suppress_result) 21006c3fb27SDimitry Andric if (auto result_var_sp = 21106c3fb27SDimitry Andric target.GetPersistentVariable(valobj_sp->GetName())) { 21206c3fb27SDimitry Andric auto language = valobj_sp->GetPreferredDisplayLanguage(); 21306c3fb27SDimitry Andric if (auto *persistent_state = 21406c3fb27SDimitry Andric target.GetPersistentExpressionStateForLanguage(language)) 21506c3fb27SDimitry Andric persistent_state->RemovePersistentVariable(result_var_sp); 21606c3fb27SDimitry Andric } 21706c3fb27SDimitry Andric 218bdd1243dSDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 219bdd1243dSDimitry Andric } else { 220bdd1243dSDimitry Andric if (valobj_sp) 221bdd1243dSDimitry Andric result.SetError(valobj_sp->GetError()); 222bdd1243dSDimitry Andric else 223bdd1243dSDimitry Andric result.AppendErrorWithFormatv( 224bdd1243dSDimitry Andric "unknown error evaluating expression `{0}`", expr); 225bdd1243dSDimitry Andric } 226bdd1243dSDimitry Andric } 227bdd1243dSDimitry Andric } 228