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