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" 26bdd1243dSDimitry Andric 275f757f3fSDimitry Andric #include <regex> 285f757f3fSDimitry Andric 29bdd1243dSDimitry Andric using namespace llvm; 30bdd1243dSDimitry Andric using namespace lldb; 31bdd1243dSDimitry Andric using namespace lldb_private; 32bdd1243dSDimitry Andric 33bdd1243dSDimitry Andric CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter) 34bdd1243dSDimitry Andric : CommandObjectRaw(interpreter, "dwim-print", 35bdd1243dSDimitry Andric "Print a variable or expression.", 36bdd1243dSDimitry Andric "dwim-print [<variable-name> | <expression>]", 37bdd1243dSDimitry Andric eCommandProcessMustBePaused | eCommandTryTargetAPILock) { 3806c3fb27SDimitry Andric 39*0fca6ea1SDimitry Andric AddSimpleArgumentList(eArgTypeVarName); 4006c3fb27SDimitry Andric 4106c3fb27SDimitry Andric m_option_group.Append(&m_format_options, 4206c3fb27SDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT | 4306c3fb27SDimitry Andric OptionGroupFormat::OPTION_GROUP_GDB_FMT, 4406c3fb27SDimitry Andric LLDB_OPT_SET_1); 4506c3fb27SDimitry Andric StringRef exclude_expr_options[] = {"debug", "top-level"}; 4606c3fb27SDimitry Andric m_option_group.Append(&m_expr_options, exclude_expr_options); 4706c3fb27SDimitry Andric m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 4806c3fb27SDimitry Andric m_option_group.Finalize(); 49bdd1243dSDimitry Andric } 50bdd1243dSDimitry Andric 5106c3fb27SDimitry Andric Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; } 5206c3fb27SDimitry Andric 535f757f3fSDimitry Andric void CommandObjectDWIMPrint::DoExecute(StringRef command, 54bdd1243dSDimitry Andric CommandReturnObject &result) { 5506c3fb27SDimitry Andric m_option_group.NotifyOptionParsingStarting(&m_exe_ctx); 5606c3fb27SDimitry Andric OptionsWithRaw args{command}; 5706c3fb27SDimitry Andric StringRef expr = args.GetRawPart(); 58bdd1243dSDimitry Andric 59bdd1243dSDimitry Andric if (expr.empty()) { 60bdd1243dSDimitry Andric result.AppendErrorWithFormatv("'{0}' takes a variable or expression", 61bdd1243dSDimitry Andric m_cmd_name); 625f757f3fSDimitry Andric return; 63bdd1243dSDimitry Andric } 64bdd1243dSDimitry Andric 6506c3fb27SDimitry Andric if (args.HasArgs()) { 6606c3fb27SDimitry Andric if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, 6706c3fb27SDimitry Andric m_exe_ctx)) 685f757f3fSDimitry Andric return; 6906c3fb27SDimitry Andric } 7006c3fb27SDimitry Andric 7106c3fb27SDimitry Andric // If the user has not specified, default to disabling persistent results. 7206c3fb27SDimitry Andric if (m_expr_options.suppress_persistent_result == eLazyBoolCalculate) 7306c3fb27SDimitry Andric m_expr_options.suppress_persistent_result = eLazyBoolYes; 7406c3fb27SDimitry Andric bool suppress_result = m_expr_options.ShouldSuppressResult(m_varobj_options); 7506c3fb27SDimitry Andric 76bdd1243dSDimitry Andric auto verbosity = GetDebugger().GetDWIMPrintVerbosity(); 77bdd1243dSDimitry Andric 7806c3fb27SDimitry Andric Target *target_ptr = m_exe_ctx.GetTargetPtr(); 7906c3fb27SDimitry Andric // Fallback to the dummy target, which can allow for expression evaluation. 8006c3fb27SDimitry Andric Target &target = target_ptr ? *target_ptr : GetDummyTarget(); 8106c3fb27SDimitry Andric 8206c3fb27SDimitry Andric EvaluateExpressionOptions eval_options = 8306c3fb27SDimitry Andric m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options); 8406c3fb27SDimitry Andric // This command manually removes the result variable, make sure expression 8506c3fb27SDimitry Andric // evaluation doesn't do it first. 8606c3fb27SDimitry Andric eval_options.SetSuppressPersistentResult(false); 8706c3fb27SDimitry Andric 8806c3fb27SDimitry Andric DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions( 8906c3fb27SDimitry Andric m_expr_options.m_verbosity, m_format_options.GetFormat()); 9006c3fb27SDimitry Andric dump_options.SetHideRootName(suppress_result); 9106c3fb27SDimitry Andric 925f757f3fSDimitry Andric bool is_po = m_varobj_options.use_objc; 935f757f3fSDimitry Andric 9406c3fb27SDimitry Andric StackFrame *frame = m_exe_ctx.GetFramePtr(); 9506c3fb27SDimitry Andric 96*0fca6ea1SDimitry Andric // Either the language was explicitly specified, or we check the frame. 975f757f3fSDimitry Andric lldb::LanguageType language = m_expr_options.language; 985f757f3fSDimitry Andric if (language == lldb::eLanguageTypeUnknown && frame) 99*0fca6ea1SDimitry Andric language = frame->GuessLanguage().AsLanguageType(); 1005f757f3fSDimitry Andric 1015f757f3fSDimitry Andric // Add a hint if object description was requested, but no description 1025f757f3fSDimitry Andric // function was implemented. 1035f757f3fSDimitry Andric auto maybe_add_hint = [&](llvm::StringRef output) { 1045f757f3fSDimitry Andric // Identify the default output of object description for Swift and 1055f757f3fSDimitry Andric // Objective-C 1065f757f3fSDimitry Andric // "<Name: 0x...>. The regex is: 1075f757f3fSDimitry Andric // - Start with "<". 1085f757f3fSDimitry Andric // - Followed by 1 or more non-whitespace characters. 1095f757f3fSDimitry Andric // - Followed by ": 0x". 1105f757f3fSDimitry Andric // - Followed by 5 or more hex digits. 1115f757f3fSDimitry Andric // - Followed by ">". 1125f757f3fSDimitry Andric // - End with zero or more whitespace characters. 1135f757f3fSDimitry Andric const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$"); 1145f757f3fSDimitry Andric 1155f757f3fSDimitry Andric if (GetDebugger().GetShowDontUsePoHint() && target_ptr && 1165f757f3fSDimitry Andric (language == lldb::eLanguageTypeSwift || 1175f757f3fSDimitry Andric language == lldb::eLanguageTypeObjC) && 1185f757f3fSDimitry Andric std::regex_match(output.data(), swift_class_regex)) { 1195f757f3fSDimitry Andric 1205f757f3fSDimitry Andric static bool note_shown = false; 1215f757f3fSDimitry Andric if (note_shown) 1225f757f3fSDimitry Andric return; 1235f757f3fSDimitry Andric 1245f757f3fSDimitry Andric result.GetOutputStream() 1255f757f3fSDimitry Andric << "note: object description requested, but type doesn't implement " 1265f757f3fSDimitry Andric "a custom object description. Consider using \"p\" instead of " 1275f757f3fSDimitry Andric "\"po\" (this note will only be shown once per debug session).\n"; 1285f757f3fSDimitry Andric note_shown = true; 1295f757f3fSDimitry Andric } 1305f757f3fSDimitry Andric }; 1315f757f3fSDimitry Andric 132*0fca6ea1SDimitry Andric // Dump `valobj` according to whether `po` was requested or not. 133*0fca6ea1SDimitry Andric auto dump_val_object = [&](ValueObject &valobj) { 134*0fca6ea1SDimitry Andric if (is_po) { 135*0fca6ea1SDimitry Andric StreamString temp_result_stream; 136*0fca6ea1SDimitry Andric if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) { 137*0fca6ea1SDimitry Andric result.AppendError(toString(std::move(error))); 138*0fca6ea1SDimitry Andric return; 139*0fca6ea1SDimitry Andric } 140*0fca6ea1SDimitry Andric llvm::StringRef output = temp_result_stream.GetString(); 141*0fca6ea1SDimitry Andric maybe_add_hint(output); 142*0fca6ea1SDimitry Andric result.GetOutputStream() << output; 143*0fca6ea1SDimitry Andric } else { 144*0fca6ea1SDimitry Andric llvm::Error error = 145*0fca6ea1SDimitry Andric valobj.Dump(result.GetOutputStream(), dump_options); 146*0fca6ea1SDimitry Andric if (error) { 147*0fca6ea1SDimitry Andric result.AppendError(toString(std::move(error))); 148*0fca6ea1SDimitry Andric return; 149*0fca6ea1SDimitry Andric } 150*0fca6ea1SDimitry Andric } 151*0fca6ea1SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 152*0fca6ea1SDimitry Andric }; 153*0fca6ea1SDimitry Andric 154bdd1243dSDimitry Andric // First, try `expr` as the name of a frame variable. 15506c3fb27SDimitry Andric if (frame) { 156bdd1243dSDimitry Andric auto valobj_sp = frame->FindVariable(ConstString(expr)); 157bdd1243dSDimitry Andric if (valobj_sp && valobj_sp->GetError().Success()) { 15806c3fb27SDimitry Andric if (!suppress_result) { 15906c3fb27SDimitry Andric if (auto persisted_valobj = valobj_sp->Persist()) 16006c3fb27SDimitry Andric valobj_sp = persisted_valobj; 16106c3fb27SDimitry Andric } 16206c3fb27SDimitry Andric 16306c3fb27SDimitry Andric if (verbosity == eDWIMPrintVerbosityFull) { 16406c3fb27SDimitry Andric StringRef flags; 16506c3fb27SDimitry Andric if (args.HasArgs()) 16606c3fb27SDimitry Andric flags = args.GetArgString(); 16706c3fb27SDimitry Andric result.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`", 16806c3fb27SDimitry Andric flags, expr); 16906c3fb27SDimitry Andric } 17006c3fb27SDimitry Andric 171*0fca6ea1SDimitry Andric dump_val_object(*valobj_sp); 1725f757f3fSDimitry Andric return; 173bdd1243dSDimitry Andric } 174bdd1243dSDimitry Andric } 175bdd1243dSDimitry Andric 176*0fca6ea1SDimitry Andric // Second, try `expr` as a persistent variable. 177*0fca6ea1SDimitry Andric if (expr.starts_with("$")) 178*0fca6ea1SDimitry Andric if (auto *state = target.GetPersistentExpressionStateForLanguage(language)) 179*0fca6ea1SDimitry Andric if (auto var_sp = state->GetVariable(expr)) 180*0fca6ea1SDimitry Andric if (auto valobj_sp = var_sp->GetValueObject()) { 181*0fca6ea1SDimitry Andric dump_val_object(*valobj_sp); 182*0fca6ea1SDimitry Andric return; 183*0fca6ea1SDimitry Andric } 184*0fca6ea1SDimitry Andric 185*0fca6ea1SDimitry Andric // Third, and lastly, try `expr` as a source expression to evaluate. 186bdd1243dSDimitry Andric { 187bdd1243dSDimitry Andric auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); 188bdd1243dSDimitry Andric ValueObjectSP valobj_sp; 1895f757f3fSDimitry Andric std::string fixed_expression; 1905f757f3fSDimitry Andric 1915f757f3fSDimitry Andric ExpressionResults expr_result = target.EvaluateExpression( 1925f757f3fSDimitry Andric expr, exe_scope, valobj_sp, eval_options, &fixed_expression); 1935f757f3fSDimitry Andric 1945f757f3fSDimitry Andric // Only mention Fix-Its if the expression evaluator applied them. 1955f757f3fSDimitry Andric // Compiler errors refer to the final expression after applying Fix-It(s). 1965f757f3fSDimitry Andric if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) { 1975f757f3fSDimitry Andric Stream &error_stream = result.GetErrorStream(); 1985f757f3fSDimitry Andric error_stream << " Evaluated this expression after applying Fix-It(s):\n"; 1995f757f3fSDimitry Andric error_stream << " " << fixed_expression << "\n"; 2005f757f3fSDimitry Andric } 2015f757f3fSDimitry Andric 202*0fca6ea1SDimitry Andric // If the expression failed, return an error. 203*0fca6ea1SDimitry Andric if (expr_result != eExpressionCompleted) { 204*0fca6ea1SDimitry Andric if (valobj_sp) 205*0fca6ea1SDimitry Andric result.SetError(valobj_sp->GetError()); 206*0fca6ea1SDimitry Andric else 207*0fca6ea1SDimitry Andric result.AppendErrorWithFormatv( 208*0fca6ea1SDimitry Andric "unknown error evaluating expression `{0}`", expr); 209*0fca6ea1SDimitry Andric return; 210*0fca6ea1SDimitry Andric } 211*0fca6ea1SDimitry Andric 21206c3fb27SDimitry Andric if (verbosity != eDWIMPrintVerbosityNone) { 21306c3fb27SDimitry Andric StringRef flags; 21406c3fb27SDimitry Andric if (args.HasArgs()) 21506c3fb27SDimitry Andric flags = args.GetArgStringWithDelimiter(); 21606c3fb27SDimitry Andric result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags, 21706c3fb27SDimitry Andric expr); 21806c3fb27SDimitry Andric } 21906c3fb27SDimitry Andric 220*0fca6ea1SDimitry Andric if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) 221*0fca6ea1SDimitry Andric dump_val_object(*valobj_sp); 222*0fca6ea1SDimitry Andric else 223*0fca6ea1SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 22406c3fb27SDimitry Andric 22506c3fb27SDimitry Andric if (suppress_result) 22606c3fb27SDimitry Andric if (auto result_var_sp = 22706c3fb27SDimitry Andric target.GetPersistentVariable(valobj_sp->GetName())) { 22806c3fb27SDimitry Andric auto language = valobj_sp->GetPreferredDisplayLanguage(); 22906c3fb27SDimitry Andric if (auto *persistent_state = 23006c3fb27SDimitry Andric target.GetPersistentExpressionStateForLanguage(language)) 23106c3fb27SDimitry Andric persistent_state->RemovePersistentVariable(result_var_sp); 23206c3fb27SDimitry Andric } 233bdd1243dSDimitry Andric } 234bdd1243dSDimitry Andric } 235