1 | //===-- CommandObjectExpression.cpp ---------------------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "CommandObjectExpression.h" |
10 | #include "lldb/Core/Debugger.h" |
11 | #include "lldb/Expression/ExpressionVariable.h" |
12 | #include "lldb/Expression/REPL.h" |
13 | #include "lldb/Expression/UserExpression.h" |
14 | #include "lldb/Host/OptionParser.h" |
15 | #include "lldb/Host/StreamFile.h" |
16 | #include "lldb/Interpreter/CommandInterpreter.h" |
17 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
18 | #include "lldb/Interpreter/CommandReturnObject.h" |
19 | #include "lldb/Interpreter/OptionArgParser.h" |
20 | #include "lldb/Target/Language.h" |
21 | #include "lldb/Target/Process.h" |
22 | #include "lldb/Target/StackFrame.h" |
23 | #include "lldb/Target/Target.h" |
24 | #include "lldb/Utility/DiagnosticsRendering.h" |
25 | #include "lldb/lldb-enumerations.h" |
26 | #include "lldb/lldb-forward.h" |
27 | #include "lldb/lldb-private-enumerations.h" |
28 | |
29 | using namespace lldb; |
30 | using namespace lldb_private; |
31 | |
32 | CommandObjectExpression::CommandOptions::CommandOptions() = default; |
33 | |
34 | CommandObjectExpression::CommandOptions::~CommandOptions() = default; |
35 | |
36 | #define LLDB_OPTIONS_expression |
37 | #include "CommandOptions.inc" |
38 | |
39 | Status CommandObjectExpression::CommandOptions::SetOptionValue( |
40 | uint32_t option_idx, llvm::StringRef option_arg, |
41 | ExecutionContext *execution_context) { |
42 | Status error; |
43 | |
44 | const int short_option = GetDefinitions()[option_idx].short_option; |
45 | |
46 | switch (short_option) { |
47 | case 'l': |
48 | language = Language::GetLanguageTypeFromString(string: option_arg); |
49 | if (language == eLanguageTypeUnknown) { |
50 | StreamString sstr; |
51 | sstr.Printf(format: "unknown language type: '%s' for expression. " |
52 | "List of supported languages:\n", |
53 | option_arg.str().c_str()); |
54 | |
55 | Language::PrintSupportedLanguagesForExpressions(s&: sstr, prefix: " ", suffix: "\n"); |
56 | error = Status(sstr.GetString().str()); |
57 | } |
58 | break; |
59 | |
60 | case 'a': { |
61 | bool success; |
62 | bool result; |
63 | result = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
64 | if (!success) |
65 | error = Status::FromErrorStringWithFormat( |
66 | format: "invalid all-threads value setting: \"%s\"", |
67 | option_arg.str().c_str()); |
68 | else |
69 | try_all_threads = result; |
70 | } break; |
71 | |
72 | case 'i': { |
73 | bool success; |
74 | bool tmp_value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
75 | if (success) |
76 | ignore_breakpoints = tmp_value; |
77 | else |
78 | error = Status::FromErrorStringWithFormat( |
79 | format: "could not convert \"%s\" to a boolean value.", |
80 | option_arg.str().c_str()); |
81 | break; |
82 | } |
83 | |
84 | case 'j': { |
85 | bool success; |
86 | bool tmp_value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
87 | if (success) |
88 | allow_jit = tmp_value; |
89 | else |
90 | error = Status::FromErrorStringWithFormat( |
91 | format: "could not convert \"%s\" to a boolean value.", |
92 | option_arg.str().c_str()); |
93 | break; |
94 | } |
95 | |
96 | case 't': |
97 | if (option_arg.getAsInteger(Radix: 0, Result&: timeout)) { |
98 | timeout = 0; |
99 | error = Status::FromErrorStringWithFormat( |
100 | format: "invalid timeout setting \"%s\"", option_arg.str().c_str()); |
101 | } |
102 | break; |
103 | |
104 | case 'u': { |
105 | bool success; |
106 | bool tmp_value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
107 | if (success) |
108 | unwind_on_error = tmp_value; |
109 | else |
110 | error = Status::FromErrorStringWithFormat( |
111 | format: "could not convert \"%s\" to a boolean value.", |
112 | option_arg.str().c_str()); |
113 | break; |
114 | } |
115 | |
116 | case 'v': |
117 | if (option_arg.empty()) { |
118 | m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull; |
119 | break; |
120 | } |
121 | m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity) |
122 | OptionArgParser::ToOptionEnum( |
123 | s: option_arg, enum_values: GetDefinitions()[option_idx].enum_values, fail_value: 0, error); |
124 | if (!error.Success()) |
125 | error = Status::FromErrorStringWithFormat( |
126 | format: "unrecognized value for description-verbosity '%s'", |
127 | option_arg.str().c_str()); |
128 | break; |
129 | |
130 | case 'g': |
131 | debug = true; |
132 | unwind_on_error = false; |
133 | ignore_breakpoints = false; |
134 | break; |
135 | |
136 | case 'p': |
137 | top_level = true; |
138 | break; |
139 | |
140 | case 'X': { |
141 | bool success; |
142 | bool tmp_value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
143 | if (success) |
144 | auto_apply_fixits = tmp_value ? eLazyBoolYes : eLazyBoolNo; |
145 | else |
146 | error = Status::FromErrorStringWithFormat( |
147 | format: "could not convert \"%s\" to a boolean value.", |
148 | option_arg.str().c_str()); |
149 | break; |
150 | } |
151 | |
152 | case '\x01': { |
153 | bool success; |
154 | bool persist_result = |
155 | OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
156 | if (success) |
157 | suppress_persistent_result = !persist_result ? eLazyBoolYes : eLazyBoolNo; |
158 | else |
159 | error = Status::FromErrorStringWithFormat( |
160 | format: "could not convert \"%s\" to a boolean value.", |
161 | option_arg.str().c_str()); |
162 | break; |
163 | } |
164 | |
165 | default: |
166 | llvm_unreachable("Unimplemented option"); |
167 | } |
168 | |
169 | return error; |
170 | } |
171 | |
172 | void CommandObjectExpression::CommandOptions::OptionParsingStarting( |
173 | ExecutionContext *execution_context) { |
174 | auto process_sp = |
175 | execution_context ? execution_context->GetProcessSP() : ProcessSP(); |
176 | if (process_sp) { |
177 | ignore_breakpoints = process_sp->GetIgnoreBreakpointsInExpressions(); |
178 | unwind_on_error = process_sp->GetUnwindOnErrorInExpressions(); |
179 | } else { |
180 | ignore_breakpoints = true; |
181 | unwind_on_error = true; |
182 | } |
183 | |
184 | show_summary = true; |
185 | try_all_threads = true; |
186 | timeout = 0; |
187 | debug = false; |
188 | language = eLanguageTypeUnknown; |
189 | m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact; |
190 | auto_apply_fixits = eLazyBoolCalculate; |
191 | top_level = false; |
192 | allow_jit = true; |
193 | suppress_persistent_result = eLazyBoolCalculate; |
194 | } |
195 | |
196 | llvm::ArrayRef<OptionDefinition> |
197 | CommandObjectExpression::CommandOptions::GetDefinitions() { |
198 | return llvm::ArrayRef(g_expression_options); |
199 | } |
200 | |
201 | EvaluateExpressionOptions |
202 | CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions( |
203 | const Target &target, const OptionGroupValueObjectDisplay &display_opts) { |
204 | EvaluateExpressionOptions options; |
205 | options.SetCoerceToId(display_opts.use_objc); |
206 | options.SetUnwindOnError(unwind_on_error); |
207 | options.SetIgnoreBreakpoints(ignore_breakpoints); |
208 | options.SetKeepInMemory(true); |
209 | options.SetUseDynamic(display_opts.use_dynamic); |
210 | options.SetTryAllThreads(try_all_threads); |
211 | options.SetDebug(debug); |
212 | options.SetLanguage(language); |
213 | options.SetExecutionPolicy( |
214 | allow_jit ? EvaluateExpressionOptions::default_execution_policy |
215 | : lldb_private::eExecutionPolicyNever); |
216 | |
217 | bool auto_apply_fixits; |
218 | if (this->auto_apply_fixits == eLazyBoolCalculate) |
219 | auto_apply_fixits = target.GetEnableAutoApplyFixIts(); |
220 | else |
221 | auto_apply_fixits = this->auto_apply_fixits == eLazyBoolYes; |
222 | |
223 | options.SetAutoApplyFixIts(auto_apply_fixits); |
224 | options.SetRetriesWithFixIts(target.GetNumberOfRetriesWithFixits()); |
225 | |
226 | if (top_level) |
227 | options.SetExecutionPolicy(eExecutionPolicyTopLevel); |
228 | |
229 | // If there is any chance we are going to stop and want to see what went |
230 | // wrong with our expression, we should generate debug info |
231 | if (!ignore_breakpoints || !unwind_on_error) |
232 | options.SetGenerateDebugInfo(true); |
233 | |
234 | if (timeout > 0) |
235 | options.SetTimeout(std::chrono::microseconds(timeout)); |
236 | else |
237 | options.SetTimeout(std::nullopt); |
238 | return options; |
239 | } |
240 | |
241 | bool CommandObjectExpression::CommandOptions::ShouldSuppressResult( |
242 | const OptionGroupValueObjectDisplay &display_opts) const { |
243 | // Explicitly disabling persistent results takes precedence over the |
244 | // m_verbosity/use_objc logic. |
245 | if (suppress_persistent_result != eLazyBoolCalculate) |
246 | return suppress_persistent_result == eLazyBoolYes; |
247 | |
248 | return display_opts.use_objc && |
249 | m_verbosity == eLanguageRuntimeDescriptionDisplayVerbosityCompact; |
250 | } |
251 | |
252 | CommandObjectExpression::CommandObjectExpression( |
253 | CommandInterpreter &interpreter) |
254 | : CommandObjectRaw(interpreter, "expression", |
255 | "Evaluate an expression on the current " |
256 | "thread. Displays any returned value " |
257 | "with LLDB's default formatting.", |
258 | "", |
259 | eCommandProcessMustBePaused | eCommandTryTargetAPILock), |
260 | IOHandlerDelegate(IOHandlerDelegate::Completion::Expression), |
261 | m_format_options(eFormatDefault), |
262 | m_repl_option(LLDB_OPT_SET_1, false, "repl", 'r', "Drop into REPL", false, |
263 | true), |
264 | m_expr_line_count(0) { |
265 | SetHelpLong( |
266 | R"( |
267 | Single and multi-line expressions: |
268 | |
269 | )" |
270 | " The expression provided on the command line must be a complete expression \ |
271 | with no newlines. To evaluate a multi-line expression, \ |
272 | hit a return after an empty expression, and lldb will enter the multi-line expression editor. \ |
273 | Hit return on an empty line to end the multi-line expression." |
274 | |
275 | R"( |
276 | |
277 | Timeouts: |
278 | |
279 | )" |
280 | " If the expression can be evaluated statically (without running code) then it will be. \ |
281 | Otherwise, by default the expression will run on the current thread with a short timeout: \ |
282 | currently .25 seconds. If it doesn't return in that time, the evaluation will be interrupted \ |
283 | and resumed with all threads running. You can use the -a option to disable retrying on all \ |
284 | threads. You can use the -t option to set a shorter timeout." |
285 | R"( |
286 | |
287 | User defined variables: |
288 | |
289 | )" |
290 | " You can define your own variables for convenience or to be used in subsequent expressions. \ |
291 | You define them the same way you would define variables in C. If the first character of \ |
292 | your user defined variable is a $, then the variable's value will be available in future \ |
293 | expressions, otherwise it will just be available in the current expression." |
294 | R"( |
295 | |
296 | Continuing evaluation after a breakpoint: |
297 | |
298 | )" |
299 | " If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once \ |
300 | you are done with your investigation, you can either remove the expression execution frames \ |
301 | from the stack with \"thread return -x\" or if you are still interested in the expression result \ |
302 | you can issue the \"continue\" command and the expression evaluation will complete and the \ |
303 | expression result will be available using the \"thread.completed-expression\" key in the thread \ |
304 | format." |
305 | |
306 | R"( |
307 | |
308 | Examples: |
309 | |
310 | expr my_struct->a = my_array[3] |
311 | expr -f bin -- (index * 8) + 5 |
312 | expr unsigned int $foo = 5 |
313 | expr char c[] = \"foo\"; c[0])"); |
314 | |
315 | AddSimpleArgumentList(arg_type: eArgTypeExpression); |
316 | |
317 | // Add the "--format" and "--gdb-format" |
318 | m_option_group.Append(group: &m_format_options, |
319 | src_mask: OptionGroupFormat::OPTION_GROUP_FORMAT | |
320 | OptionGroupFormat::OPTION_GROUP_GDB_FMT, |
321 | LLDB_OPT_SET_1); |
322 | m_option_group.Append(group: &m_command_options); |
323 | m_option_group.Append(group: &m_varobj_options, LLDB_OPT_SET_ALL, |
324 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2); |
325 | m_option_group.Append(group: &m_repl_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); |
326 | m_option_group.Finalize(); |
327 | } |
328 | |
329 | CommandObjectExpression::~CommandObjectExpression() = default; |
330 | |
331 | Options *CommandObjectExpression::GetOptions() { return &m_option_group; } |
332 | |
333 | void CommandObjectExpression::HandleCompletion(CompletionRequest &request) { |
334 | EvaluateExpressionOptions options; |
335 | options.SetCoerceToId(m_varobj_options.use_objc); |
336 | options.SetLanguage(m_command_options.language); |
337 | options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever); |
338 | options.SetAutoApplyFixIts(false); |
339 | options.SetGenerateDebugInfo(false); |
340 | |
341 | ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); |
342 | |
343 | // Get out before we start doing things that expect a valid frame pointer. |
344 | if (exe_ctx.GetFramePtr() == nullptr) |
345 | return; |
346 | |
347 | Target *exe_target = exe_ctx.GetTargetPtr(); |
348 | Target &target = exe_target ? *exe_target : GetDummyTarget(); |
349 | |
350 | unsigned cursor_pos = request.GetRawCursorPos(); |
351 | // Get the full user input including the suffix. The suffix is necessary |
352 | // as OptionsWithRaw will use it to detect if the cursor is cursor is in the |
353 | // argument part of in the raw input part of the arguments. If we cut of |
354 | // of the suffix then "expr -arg[cursor] --" would interpret the "-arg" as |
355 | // the raw input (as the "--" is hidden in the suffix). |
356 | llvm::StringRef code = request.GetRawLineWithUnusedSuffix(); |
357 | |
358 | const std::size_t original_code_size = code.size(); |
359 | |
360 | // Remove the first token which is 'expr' or some alias/abbreviation of that. |
361 | code = llvm::getToken(Source: code).second.ltrim(); |
362 | OptionsWithRaw args(code); |
363 | code = args.GetRawPart(); |
364 | |
365 | // The position where the expression starts in the command line. |
366 | assert(original_code_size >= code.size()); |
367 | std::size_t raw_start = original_code_size - code.size(); |
368 | |
369 | // Check if the cursor is actually in the expression string, and if not, we |
370 | // exit. |
371 | // FIXME: We should complete the options here. |
372 | if (cursor_pos < raw_start) |
373 | return; |
374 | |
375 | // Make the cursor_pos again relative to the start of the code string. |
376 | assert(cursor_pos >= raw_start); |
377 | cursor_pos -= raw_start; |
378 | |
379 | auto language = exe_ctx.GetFrameRef().GetLanguage(); |
380 | |
381 | Status error; |
382 | lldb::UserExpressionSP expr(target.GetUserExpressionForLanguage( |
383 | expr: code, prefix: llvm::StringRef(), language, desired_type: UserExpression::eResultTypeAny, |
384 | options, ctx_obj: nullptr, error)); |
385 | if (error.Fail()) |
386 | return; |
387 | |
388 | expr->Complete(exe_ctx, request, complete_pos: cursor_pos); |
389 | } |
390 | |
391 | static lldb_private::Status |
392 | CanBeUsedForElementCountPrinting(ValueObject &valobj) { |
393 | CompilerType type(valobj.GetCompilerType()); |
394 | CompilerType pointee; |
395 | if (!type.IsPointerType(pointee_type: &pointee)) |
396 | return Status::FromErrorString(str: "as it does not refer to a pointer"); |
397 | if (pointee.IsVoidType()) |
398 | return Status::FromErrorString(str: "as it refers to a pointer to void"); |
399 | return Status(); |
400 | } |
401 | |
402 | bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr, |
403 | Stream &output_stream, |
404 | Stream &error_stream, |
405 | CommandReturnObject &result) { |
406 | // Don't use m_exe_ctx as this might be called asynchronously after the |
407 | // command object DoExecute has finished when doing multi-line expression |
408 | // that use an input reader... |
409 | ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); |
410 | Target *exe_target = exe_ctx.GetTargetPtr(); |
411 | Target &target = exe_target ? *exe_target : GetDummyTarget(); |
412 | |
413 | lldb::ValueObjectSP result_valobj_sp; |
414 | StackFrame *frame = exe_ctx.GetFramePtr(); |
415 | |
416 | if (m_command_options.top_level && !m_command_options.allow_jit) { |
417 | result.AppendErrorWithFormat( |
418 | format: "Can't disable JIT compilation for top-level expressions.\n"); |
419 | return false; |
420 | } |
421 | |
422 | EvaluateExpressionOptions eval_options = |
423 | m_command_options.GetEvaluateExpressionOptions(target, display_opts: m_varobj_options); |
424 | // This command manually removes the result variable, make sure expression |
425 | // evaluation doesn't do it first. |
426 | eval_options.SetSuppressPersistentResult(false); |
427 | |
428 | ExpressionResults success = target.EvaluateExpression( |
429 | expression: expr, exe_scope: frame, result_valobj_sp, options: eval_options, fixed_expression: &m_fixed_expression); |
430 | |
431 | // Only mention Fix-Its if the expression evaluator applied them. |
432 | // Compiler errors refer to the final expression after applying Fix-It(s). |
433 | if (!m_fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) { |
434 | error_stream << " Evaluated this expression after applying Fix-It(s):\n"; |
435 | error_stream << " "<< m_fixed_expression << "\n"; |
436 | } |
437 | |
438 | if (result_valobj_sp) { |
439 | result.GetValueObjectList().Append(val_obj_sp: result_valobj_sp); |
440 | |
441 | Format format = m_format_options.GetFormat(); |
442 | |
443 | if (result_valobj_sp->GetError().Success()) { |
444 | if (format != eFormatVoid) { |
445 | if (format != eFormatDefault) |
446 | result_valobj_sp->SetFormat(format); |
447 | |
448 | if (m_varobj_options.elem_count > 0) { |
449 | Status error(CanBeUsedForElementCountPrinting(valobj&: *result_valobj_sp)); |
450 | if (error.Fail()) { |
451 | result.AppendErrorWithFormat( |
452 | format: "expression cannot be used with --element-count %s\n", |
453 | error.AsCString(default_error_str: "")); |
454 | return false; |
455 | } |
456 | } |
457 | |
458 | bool suppress_result = |
459 | m_command_options.ShouldSuppressResult(display_opts: m_varobj_options); |
460 | |
461 | DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( |
462 | lang_descr_verbosity: m_command_options.m_verbosity, format)); |
463 | options.SetHideRootName(suppress_result); |
464 | options.SetVariableFormatDisplayLanguage( |
465 | result_valobj_sp->GetPreferredDisplayLanguage()); |
466 | |
467 | if (llvm::Error error = |
468 | result_valobj_sp->Dump(s&: output_stream, options)) { |
469 | result.AppendError(in_string: toString(E: std::move(error))); |
470 | return false; |
471 | } |
472 | |
473 | if (suppress_result) |
474 | if (auto result_var_sp = |
475 | target.GetPersistentVariable(name: result_valobj_sp->GetName())) { |
476 | auto language = result_valobj_sp->GetPreferredDisplayLanguage(); |
477 | if (auto *persistent_state = |
478 | target.GetPersistentExpressionStateForLanguage(language)) |
479 | persistent_state->RemovePersistentVariable(variable: result_var_sp); |
480 | } |
481 | result.SetStatus(eReturnStatusSuccessFinishResult); |
482 | } |
483 | } else { |
484 | if (result_valobj_sp->GetError().GetError() == |
485 | UserExpression::kNoResult) { |
486 | if (format != eFormatVoid && GetDebugger().GetNotifyVoid()) { |
487 | error_stream.PutCString(cstr: "(void)\n"); |
488 | } |
489 | |
490 | result.SetStatus(eReturnStatusSuccessFinishResult); |
491 | } else { |
492 | result.SetStatus(eReturnStatusFailed); |
493 | result.SetError(result_valobj_sp->GetError().ToError()); |
494 | } |
495 | } |
496 | } else { |
497 | error_stream.Printf(format: "error: unknown error\n"); |
498 | } |
499 | |
500 | return (success != eExpressionSetupError && |
501 | success != eExpressionParseError); |
502 | } |
503 | |
504 | void CommandObjectExpression::IOHandlerInputComplete(IOHandler &io_handler, |
505 | std::string &line) { |
506 | io_handler.SetIsDone(true); |
507 | StreamSP output_stream = |
508 | GetCommandInterpreter().GetDebugger().GetAsyncOutputStream(); |
509 | StreamSP error_stream = |
510 | GetCommandInterpreter().GetDebugger().GetAsyncErrorStream(); |
511 | |
512 | CommandReturnObject return_obj( |
513 | GetCommandInterpreter().GetDebugger().GetUseColor()); |
514 | EvaluateExpression(expr: line.c_str(), output_stream&: *output_stream, error_stream&: *error_stream, result&: return_obj); |
515 | |
516 | output_stream->Flush(); |
517 | *error_stream << return_obj.GetErrorString(); |
518 | } |
519 | |
520 | bool CommandObjectExpression::IOHandlerIsInputComplete(IOHandler &io_handler, |
521 | StringList &lines) { |
522 | // An empty lines is used to indicate the end of input |
523 | const size_t num_lines = lines.GetSize(); |
524 | if (num_lines > 0 && lines[num_lines - 1].empty()) { |
525 | // Remove the last empty line from "lines" so it doesn't appear in our |
526 | // resulting input and return true to indicate we are done getting lines |
527 | lines.PopBack(); |
528 | return true; |
529 | } |
530 | return false; |
531 | } |
532 | |
533 | void CommandObjectExpression::GetMultilineExpression() { |
534 | m_expr_lines.clear(); |
535 | m_expr_line_count = 0; |
536 | |
537 | Debugger &debugger = GetCommandInterpreter().GetDebugger(); |
538 | bool color_prompt = debugger.GetUseColor(); |
539 | const bool multiple_lines = true; // Get multiple lines |
540 | IOHandlerSP io_handler_sp( |
541 | new IOHandlerEditline(debugger, IOHandler::Type::Expression, |
542 | "lldb-expr", // Name of input reader for history |
543 | llvm::StringRef(), // No prompt |
544 | llvm::StringRef(), // Continuation prompt |
545 | multiple_lines, color_prompt, |
546 | 1, // Show line numbers starting at 1 |
547 | *this)); |
548 | |
549 | if (LockableStreamFileSP output_sp = io_handler_sp->GetOutputStreamFileSP()) { |
550 | LockedStreamFile locked_stream = output_sp->Lock(); |
551 | locked_stream.PutCString( |
552 | cstr: "Enter expressions, then terminate with an empty line to evaluate:\n"); |
553 | } |
554 | debugger.RunIOHandlerAsync(reader_sp: io_handler_sp); |
555 | } |
556 | |
557 | static EvaluateExpressionOptions |
558 | GetExprOptions(ExecutionContext &ctx, |
559 | CommandObjectExpression::CommandOptions command_options) { |
560 | command_options.OptionParsingStarting(execution_context: &ctx); |
561 | |
562 | // Default certain settings for REPL regardless of the global settings. |
563 | command_options.unwind_on_error = false; |
564 | command_options.ignore_breakpoints = false; |
565 | command_options.debug = false; |
566 | |
567 | EvaluateExpressionOptions expr_options; |
568 | expr_options.SetUnwindOnError(command_options.unwind_on_error); |
569 | expr_options.SetIgnoreBreakpoints(command_options.ignore_breakpoints); |
570 | expr_options.SetTryAllThreads(command_options.try_all_threads); |
571 | |
572 | if (command_options.timeout > 0) |
573 | expr_options.SetTimeout(std::chrono::microseconds(command_options.timeout)); |
574 | else |
575 | expr_options.SetTimeout(std::nullopt); |
576 | |
577 | return expr_options; |
578 | } |
579 | |
580 | void CommandObjectExpression::DoExecute(llvm::StringRef command, |
581 | CommandReturnObject &result) { |
582 | m_fixed_expression.clear(); |
583 | auto exe_ctx = GetCommandInterpreter().GetExecutionContext(); |
584 | m_option_group.NotifyOptionParsingStarting(execution_context: &exe_ctx); |
585 | |
586 | if (command.empty()) { |
587 | GetMultilineExpression(); |
588 | return; |
589 | } |
590 | |
591 | OptionsWithRaw args(command); |
592 | llvm::StringRef expr = args.GetRawPart(); |
593 | |
594 | if (args.HasArgs()) { |
595 | if (!ParseOptionsAndNotify(args&: args.GetArgs(), result, group_options&: m_option_group, exe_ctx)) |
596 | return; |
597 | |
598 | if (m_repl_option.GetOptionValue().GetCurrentValue()) { |
599 | Target &target = GetTarget(); |
600 | // Drop into REPL |
601 | m_expr_lines.clear(); |
602 | m_expr_line_count = 0; |
603 | |
604 | Debugger &debugger = target.GetDebugger(); |
605 | |
606 | // Check if the LLDB command interpreter is sitting on top of a REPL |
607 | // that launched it... |
608 | if (debugger.CheckTopIOHandlerTypes(top_type: IOHandler::Type::CommandInterpreter, |
609 | second_top_type: IOHandler::Type::REPL)) { |
610 | // the LLDB command interpreter is sitting on top of a REPL that |
611 | // launched it, so just say the command interpreter is done and |
612 | // fall back to the existing REPL |
613 | m_interpreter.GetIOHandler(force_create: false)->SetIsDone(true); |
614 | } else { |
615 | // We are launching the REPL on top of the current LLDB command |
616 | // interpreter, so just push one |
617 | bool initialize = false; |
618 | Status repl_error; |
619 | REPLSP repl_sp(target.GetREPL(err&: repl_error, language: m_command_options.language, |
620 | repl_options: nullptr, can_create: false)); |
621 | |
622 | if (!repl_sp) { |
623 | initialize = true; |
624 | repl_sp = target.GetREPL(err&: repl_error, language: m_command_options.language, |
625 | repl_options: nullptr, can_create: true); |
626 | if (repl_error.Fail()) { |
627 | result.SetError(std::move(repl_error)); |
628 | return; |
629 | } |
630 | } |
631 | |
632 | if (repl_sp) { |
633 | if (initialize) { |
634 | repl_sp->SetEvaluateOptions( |
635 | GetExprOptions(ctx&: exe_ctx, command_options: m_command_options)); |
636 | repl_sp->SetFormatOptions(m_format_options); |
637 | repl_sp->SetValueObjectDisplayOptions(m_varobj_options); |
638 | } |
639 | |
640 | IOHandlerSP io_handler_sp(repl_sp->GetIOHandler()); |
641 | io_handler_sp->SetIsDone(false); |
642 | debugger.RunIOHandlerAsync(reader_sp: io_handler_sp); |
643 | } else { |
644 | repl_error = Status::FromErrorStringWithFormat( |
645 | format: "Couldn't create a REPL for %s", |
646 | Language::GetNameForLanguageType(language: m_command_options.language)); |
647 | result.SetError(std::move(repl_error)); |
648 | return; |
649 | } |
650 | } |
651 | } |
652 | // No expression following options |
653 | else if (expr.empty()) { |
654 | GetMultilineExpression(); |
655 | return; |
656 | } |
657 | } |
658 | |
659 | // Previously the indent was set up for diagnosing command line |
660 | // parsing errors. Now point it to the expression. |
661 | std::optional<uint16_t> indent; |
662 | size_t pos = m_original_command.rfind(svt: expr); |
663 | if (pos != llvm::StringRef::npos) |
664 | indent = pos; |
665 | result.SetDiagnosticIndent(indent); |
666 | |
667 | Target &target = GetTarget(); |
668 | if (EvaluateExpression(expr, output_stream&: result.GetOutputStream(), |
669 | error_stream&: result.GetErrorStream(), result)) { |
670 | |
671 | if (!m_fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) { |
672 | CommandHistory &history = m_interpreter.GetCommandHistory(); |
673 | // FIXME: Can we figure out what the user actually typed (e.g. some alias |
674 | // for expr???) |
675 | // If we can it would be nice to show that. |
676 | std::string fixed_command("expression "); |
677 | if (args.HasArgs()) { |
678 | // Add in any options that might have been in the original command: |
679 | fixed_command.append(str: std::string(args.GetArgStringWithDelimiter())); |
680 | fixed_command.append(str: m_fixed_expression); |
681 | } else |
682 | fixed_command.append(str: m_fixed_expression); |
683 | history.AppendString(str: fixed_command); |
684 | } |
685 | return; |
686 | } |
687 | result.SetStatus(eReturnStatusFailed); |
688 | } |
689 |
Definitions
- CommandOptions
- ~CommandOptions
- SetOptionValue
- OptionParsingStarting
- GetDefinitions
- GetEvaluateExpressionOptions
- ShouldSuppressResult
- CommandObjectExpression
- ~CommandObjectExpression
- GetOptions
- HandleCompletion
- CanBeUsedForElementCountPrinting
- EvaluateExpression
- IOHandlerInputComplete
- IOHandlerIsInputComplete
- GetMultilineExpression
- GetExprOptions
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more