1 //===-- SBCommandInterpreter.cpp --------------------------------*- C++ -*-===//
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 "lldb/lldb-types.h"
10 
11 #include "lldb/Interpreter/CommandInterpreter.h"
12 #include "lldb/Interpreter/CommandObjectMultiword.h"
13 #include "lldb/Interpreter/CommandReturnObject.h"
14 #include "lldb/Target/Target.h"
15 #include "lldb/Utility/Listener.h"
16 
17 #include "lldb/API/SBBroadcaster.h"
18 #include "lldb/API/SBCommandInterpreter.h"
19 #include "lldb/API/SBCommandReturnObject.h"
20 #include "lldb/API/SBEvent.h"
21 #include "lldb/API/SBExecutionContext.h"
22 #include "lldb/API/SBListener.h"
23 #include "lldb/API/SBProcess.h"
24 #include "lldb/API/SBStream.h"
25 #include "lldb/API/SBStringList.h"
26 #include "lldb/API/SBTarget.h"
27 
28 #include <memory>
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() {
34   m_opaque_up.reset(new CommandInterpreterRunOptions());
35 }
36 
37 SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() = default;
38 
39 bool SBCommandInterpreterRunOptions::GetStopOnContinue() const {
40   return m_opaque_up->GetStopOnContinue();
41 }
42 
43 void SBCommandInterpreterRunOptions::SetStopOnContinue(bool stop_on_continue) {
44   m_opaque_up->SetStopOnContinue(stop_on_continue);
45 }
46 
47 bool SBCommandInterpreterRunOptions::GetStopOnError() const {
48   return m_opaque_up->GetStopOnError();
49 }
50 
51 void SBCommandInterpreterRunOptions::SetStopOnError(bool stop_on_error) {
52   m_opaque_up->SetStopOnError(stop_on_error);
53 }
54 
55 bool SBCommandInterpreterRunOptions::GetStopOnCrash() const {
56   return m_opaque_up->GetStopOnCrash();
57 }
58 
59 void SBCommandInterpreterRunOptions::SetStopOnCrash(bool stop_on_crash) {
60   m_opaque_up->SetStopOnCrash(stop_on_crash);
61 }
62 
63 bool SBCommandInterpreterRunOptions::GetEchoCommands() const {
64   return m_opaque_up->GetEchoCommands();
65 }
66 
67 void SBCommandInterpreterRunOptions::SetEchoCommands(bool echo_commands) {
68   m_opaque_up->SetEchoCommands(echo_commands);
69 }
70 
71 bool SBCommandInterpreterRunOptions::GetEchoCommentCommands() const {
72   return m_opaque_up->GetEchoCommentCommands();
73 }
74 
75 void SBCommandInterpreterRunOptions::SetEchoCommentCommands(bool echo) {
76   m_opaque_up->SetEchoCommentCommands(echo);
77 }
78 
79 bool SBCommandInterpreterRunOptions::GetPrintResults() const {
80   return m_opaque_up->GetPrintResults();
81 }
82 
83 void SBCommandInterpreterRunOptions::SetPrintResults(bool print_results) {
84   m_opaque_up->SetPrintResults(print_results);
85 }
86 
87 bool SBCommandInterpreterRunOptions::GetAddToHistory() const {
88   return m_opaque_up->GetAddToHistory();
89 }
90 
91 void SBCommandInterpreterRunOptions::SetAddToHistory(bool add_to_history) {
92   m_opaque_up->SetAddToHistory(add_to_history);
93 }
94 
95 lldb_private::CommandInterpreterRunOptions *
96 SBCommandInterpreterRunOptions::get() const {
97   return m_opaque_up.get();
98 }
99 
100 lldb_private::CommandInterpreterRunOptions &
101 SBCommandInterpreterRunOptions::ref() const {
102   return *m_opaque_up;
103 }
104 
105 class CommandPluginInterfaceImplementation : public CommandObjectParsed {
106 public:
107   CommandPluginInterfaceImplementation(CommandInterpreter &interpreter,
108                                        const char *name,
109                                        lldb::SBCommandPluginInterface *backend,
110                                        const char *help = nullptr,
111                                        const char *syntax = nullptr,
112                                        uint32_t flags = 0)
113       : CommandObjectParsed(interpreter, name, help, syntax, flags),
114         m_backend(backend) {}
115 
116   bool IsRemovable() const override { return true; }
117 
118 protected:
119   bool DoExecute(Args &command, CommandReturnObject &result) override {
120     SBCommandReturnObject sb_return(&result);
121     SBCommandInterpreter sb_interpreter(&m_interpreter);
122     SBDebugger debugger_sb(m_interpreter.GetDebugger().shared_from_this());
123     bool ret = m_backend->DoExecute(
124         debugger_sb, (char **)command.GetArgumentVector(), sb_return);
125     sb_return.Release();
126     return ret;
127   }
128   std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
129 };
130 
131 SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
132     : m_opaque_ptr(interpreter) {
133   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
134 
135   if (log)
136     log->Printf("SBCommandInterpreter::SBCommandInterpreter (interpreter=%p)"
137                 " => SBCommandInterpreter(%p)",
138                 static_cast<void *>(interpreter),
139                 static_cast<void *>(m_opaque_ptr));
140 }
141 
142 SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs)
143     : m_opaque_ptr(rhs.m_opaque_ptr) {}
144 
145 SBCommandInterpreter::~SBCommandInterpreter() = default;
146 
147 const SBCommandInterpreter &SBCommandInterpreter::
148 operator=(const SBCommandInterpreter &rhs) {
149   m_opaque_ptr = rhs.m_opaque_ptr;
150   return *this;
151 }
152 
153 bool SBCommandInterpreter::IsValid() const { return m_opaque_ptr != nullptr; }
154 
155 bool SBCommandInterpreter::CommandExists(const char *cmd) {
156   return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd)
157                                           : false);
158 }
159 
160 bool SBCommandInterpreter::AliasExists(const char *cmd) {
161   return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->AliasExists(cmd)
162                                           : false);
163 }
164 
165 bool SBCommandInterpreter::IsActive() {
166   return (IsValid() ? m_opaque_ptr->IsActive() : false);
167 }
168 
169 bool SBCommandInterpreter::WasInterrupted() const {
170   return (IsValid() ? m_opaque_ptr->WasInterrupted() : false);
171 }
172 
173 const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) {
174   return (IsValid()
175               ? m_opaque_ptr->GetDebugger()
176                     .GetTopIOHandlerControlSequence(ch)
177                     .GetCString()
178               : nullptr);
179 }
180 
181 lldb::ReturnStatus
182 SBCommandInterpreter::HandleCommand(const char *command_line,
183                                     SBCommandReturnObject &result,
184                                     bool add_to_history) {
185   SBExecutionContext sb_exe_ctx;
186   return HandleCommand(command_line, sb_exe_ctx, result, add_to_history);
187 }
188 
189 lldb::ReturnStatus SBCommandInterpreter::HandleCommand(
190     const char *command_line, SBExecutionContext &override_context,
191     SBCommandReturnObject &result, bool add_to_history) {
192   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
193 
194   if (log)
195     log->Printf("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", "
196                 "SBCommandReturnObject(%p), add_to_history=%i)",
197                 static_cast<void *>(m_opaque_ptr), command_line,
198                 static_cast<void *>(result.get()), add_to_history);
199 
200   ExecutionContext ctx, *ctx_ptr;
201   if (override_context.get()) {
202     ctx = override_context.get()->Lock(true);
203     ctx_ptr = &ctx;
204   } else
205     ctx_ptr = nullptr;
206 
207   result.Clear();
208   if (command_line && IsValid()) {
209     result.ref().SetInteractive(false);
210     m_opaque_ptr->HandleCommand(command_line,
211                                 add_to_history ? eLazyBoolYes : eLazyBoolNo,
212                                 result.ref(), ctx_ptr);
213   } else {
214     result->AppendError(
215         "SBCommandInterpreter or the command line is not valid");
216     result->SetStatus(eReturnStatusFailed);
217   }
218 
219   // We need to get the value again, in case the command disabled the log!
220   log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
221   if (log) {
222     SBStream sstr;
223     result.GetDescription(sstr);
224     log->Printf("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", "
225                 "SBCommandReturnObject(%p): %s, add_to_history=%i) => %i",
226                 static_cast<void *>(m_opaque_ptr), command_line,
227                 static_cast<void *>(result.get()), sstr.GetData(),
228                 add_to_history, result.GetStatus());
229   }
230 
231   return result.GetStatus();
232 }
233 
234 void SBCommandInterpreter::HandleCommandsFromFile(
235     lldb::SBFileSpec &file, lldb::SBExecutionContext &override_context,
236     lldb::SBCommandInterpreterRunOptions &options,
237     lldb::SBCommandReturnObject result) {
238   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
239 
240   if (log) {
241     SBStream s;
242     file.GetDescription(s);
243     log->Printf("SBCommandInterpreter(%p)::HandleCommandsFromFile "
244                 "(file=\"%s\", SBCommandReturnObject(%p))",
245                 static_cast<void *>(m_opaque_ptr), s.GetData(),
246                 static_cast<void *>(result.get()));
247   }
248 
249   if (!IsValid()) {
250     result->AppendError("SBCommandInterpreter is not valid.");
251     result->SetStatus(eReturnStatusFailed);
252     return;
253   }
254 
255   if (!file.IsValid()) {
256     SBStream s;
257     file.GetDescription(s);
258     result->AppendErrorWithFormat("File is not valid: %s.", s.GetData());
259     result->SetStatus(eReturnStatusFailed);
260   }
261 
262   FileSpec tmp_spec = file.ref();
263   ExecutionContext ctx, *ctx_ptr;
264   if (override_context.get()) {
265     ctx = override_context.get()->Lock(true);
266     ctx_ptr = &ctx;
267   } else
268     ctx_ptr = nullptr;
269 
270   m_opaque_ptr->HandleCommandsFromFile(tmp_spec, ctx_ptr, options.ref(),
271                                        result.ref());
272 }
273 
274 int SBCommandInterpreter::HandleCompletion(
275     const char *current_line, const char *cursor, const char *last_char,
276     int match_start_point, int max_return_elements, SBStringList &matches) {
277   SBStringList dummy_descriptions;
278   return HandleCompletionWithDescriptions(
279       current_line, cursor, last_char, match_start_point, max_return_elements,
280       matches, dummy_descriptions);
281 }
282 
283 int SBCommandInterpreter::HandleCompletionWithDescriptions(
284     const char *current_line, const char *cursor, const char *last_char,
285     int match_start_point, int max_return_elements, SBStringList &matches,
286     SBStringList &descriptions) {
287   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
288   int num_completions = 0;
289 
290   // Sanity check the arguments that are passed in: cursor & last_char have to
291   // be within the current_line.
292   if (current_line == nullptr || cursor == nullptr || last_char == nullptr)
293     return 0;
294 
295   if (cursor < current_line || last_char < current_line)
296     return 0;
297 
298   size_t current_line_size = strlen(current_line);
299   if (cursor - current_line > static_cast<ptrdiff_t>(current_line_size) ||
300       last_char - current_line > static_cast<ptrdiff_t>(current_line_size))
301     return 0;
302 
303   if (log)
304     log->Printf("SBCommandInterpreter(%p)::HandleCompletion "
305                 "(current_line=\"%s\", cursor at: %" PRId64
306                 ", last char at: %" PRId64
307                 ", match_start_point: %d, max_return_elements: %d)",
308                 static_cast<void *>(m_opaque_ptr), current_line,
309                 static_cast<uint64_t>(cursor - current_line),
310                 static_cast<uint64_t>(last_char - current_line),
311                 match_start_point, max_return_elements);
312 
313   if (IsValid()) {
314     lldb_private::StringList lldb_matches, lldb_descriptions;
315     num_completions = m_opaque_ptr->HandleCompletion(
316         current_line, cursor, last_char, match_start_point, max_return_elements,
317         lldb_matches, lldb_descriptions);
318 
319     SBStringList temp_matches_list(&lldb_matches);
320     matches.AppendList(temp_matches_list);
321     SBStringList temp_descriptions_list(&lldb_descriptions);
322     descriptions.AppendList(temp_descriptions_list);
323   }
324   if (log)
325     log->Printf(
326         "SBCommandInterpreter(%p)::HandleCompletion - Found %d completions.",
327         static_cast<void *>(m_opaque_ptr), num_completions);
328 
329   return num_completions;
330 }
331 
332 int SBCommandInterpreter::HandleCompletionWithDescriptions(
333     const char *current_line, uint32_t cursor_pos, int match_start_point,
334     int max_return_elements, SBStringList &matches,
335     SBStringList &descriptions) {
336   const char *cursor = current_line + cursor_pos;
337   const char *last_char = current_line + strlen(current_line);
338   return HandleCompletionWithDescriptions(
339       current_line, cursor, last_char, match_start_point, max_return_elements,
340       matches, descriptions);
341 }
342 
343 int SBCommandInterpreter::HandleCompletion(const char *current_line,
344                                            uint32_t cursor_pos,
345                                            int match_start_point,
346                                            int max_return_elements,
347                                            lldb::SBStringList &matches) {
348   const char *cursor = current_line + cursor_pos;
349   const char *last_char = current_line + strlen(current_line);
350   return HandleCompletion(current_line, cursor, last_char, match_start_point,
351                           max_return_elements, matches);
352 }
353 
354 bool SBCommandInterpreter::HasCommands() {
355   return (IsValid() ? m_opaque_ptr->HasCommands() : false);
356 }
357 
358 bool SBCommandInterpreter::HasAliases() {
359   return (IsValid() ? m_opaque_ptr->HasAliases() : false);
360 }
361 
362 bool SBCommandInterpreter::HasAliasOptions() {
363   return (IsValid() ? m_opaque_ptr->HasAliasOptions() : false);
364 }
365 
366 SBProcess SBCommandInterpreter::GetProcess() {
367   SBProcess sb_process;
368   ProcessSP process_sp;
369   if (IsValid()) {
370     TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
371     if (target_sp) {
372       std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
373       process_sp = target_sp->GetProcessSP();
374       sb_process.SetSP(process_sp);
375     }
376   }
377   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
378 
379   if (log)
380     log->Printf("SBCommandInterpreter(%p)::GetProcess () => SBProcess(%p)",
381                 static_cast<void *>(m_opaque_ptr),
382                 static_cast<void *>(process_sp.get()));
383 
384   return sb_process;
385 }
386 
387 SBDebugger SBCommandInterpreter::GetDebugger() {
388   SBDebugger sb_debugger;
389   if (IsValid())
390     sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this());
391   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
392 
393   if (log)
394     log->Printf("SBCommandInterpreter(%p)::GetDebugger () => SBDebugger(%p)",
395                 static_cast<void *>(m_opaque_ptr),
396                 static_cast<void *>(sb_debugger.get()));
397 
398   return sb_debugger;
399 }
400 
401 bool SBCommandInterpreter::GetPromptOnQuit() {
402   return (IsValid() ? m_opaque_ptr->GetPromptOnQuit() : false);
403 }
404 
405 void SBCommandInterpreter::SetPromptOnQuit(bool b) {
406   if (IsValid())
407     m_opaque_ptr->SetPromptOnQuit(b);
408 }
409 
410 void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) {
411   if (m_opaque_ptr)
412     m_opaque_ptr->AllowExitCodeOnQuit(allow);
413 }
414 
415 bool SBCommandInterpreter::HasCustomQuitExitCode() {
416   bool exited = false;
417   if (m_opaque_ptr)
418     m_opaque_ptr->GetQuitExitCode(exited);
419   return exited;
420 }
421 
422 int SBCommandInterpreter::GetQuitStatus() {
423   bool exited = false;
424   return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0);
425 }
426 
427 void SBCommandInterpreter::ResolveCommand(const char *command_line,
428                                           SBCommandReturnObject &result) {
429   result.Clear();
430   if (command_line && IsValid()) {
431     m_opaque_ptr->ResolveCommand(command_line, result.ref());
432   } else {
433     result->AppendError(
434         "SBCommandInterpreter or the command line is not valid");
435     result->SetStatus(eReturnStatusFailed);
436   }
437 }
438 
439 CommandInterpreter *SBCommandInterpreter::get() { return m_opaque_ptr; }
440 
441 CommandInterpreter &SBCommandInterpreter::ref() {
442   assert(m_opaque_ptr);
443   return *m_opaque_ptr;
444 }
445 
446 void SBCommandInterpreter::reset(
447     lldb_private::CommandInterpreter *interpreter) {
448   m_opaque_ptr = interpreter;
449 }
450 
451 void SBCommandInterpreter::SourceInitFileInHomeDirectory(
452     SBCommandReturnObject &result) {
453   result.Clear();
454   if (IsValid()) {
455     TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
456     std::unique_lock<std::recursive_mutex> lock;
457     if (target_sp)
458       lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
459     m_opaque_ptr->SourceInitFile(false, result.ref());
460   } else {
461     result->AppendError("SBCommandInterpreter is not valid");
462     result->SetStatus(eReturnStatusFailed);
463   }
464   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
465 
466   if (log)
467     log->Printf("SBCommandInterpreter(%p)::SourceInitFileInHomeDirectory "
468                 "(&SBCommandReturnObject(%p))",
469                 static_cast<void *>(m_opaque_ptr),
470                 static_cast<void *>(result.get()));
471 }
472 
473 void SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory(
474     SBCommandReturnObject &result) {
475   result.Clear();
476   if (IsValid()) {
477     TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
478     std::unique_lock<std::recursive_mutex> lock;
479     if (target_sp)
480       lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
481     m_opaque_ptr->SourceInitFile(true, result.ref());
482   } else {
483     result->AppendError("SBCommandInterpreter is not valid");
484     result->SetStatus(eReturnStatusFailed);
485   }
486   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
487 
488   if (log)
489     log->Printf(
490         "SBCommandInterpreter(%p)::SourceInitFileInCurrentWorkingDirectory "
491         "(&SBCommandReturnObject(%p))",
492         static_cast<void *>(m_opaque_ptr), static_cast<void *>(result.get()));
493 }
494 
495 SBBroadcaster SBCommandInterpreter::GetBroadcaster() {
496   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
497 
498   SBBroadcaster broadcaster(m_opaque_ptr, false);
499 
500   if (log)
501     log->Printf(
502         "SBCommandInterpreter(%p)::GetBroadcaster() => SBBroadcaster(%p)",
503         static_cast<void *>(m_opaque_ptr),
504         static_cast<void *>(broadcaster.get()));
505 
506   return broadcaster;
507 }
508 
509 const char *SBCommandInterpreter::GetBroadcasterClass() {
510   return CommandInterpreter::GetStaticBroadcasterClass().AsCString();
511 }
512 
513 const char *SBCommandInterpreter::GetArgumentTypeAsCString(
514     const lldb::CommandArgumentType arg_type) {
515   return CommandObject::GetArgumentTypeAsCString(arg_type);
516 }
517 
518 const char *SBCommandInterpreter::GetArgumentDescriptionAsCString(
519     const lldb::CommandArgumentType arg_type) {
520   return CommandObject::GetArgumentDescriptionAsCString(arg_type);
521 }
522 
523 bool SBCommandInterpreter::EventIsCommandInterpreterEvent(
524     const lldb::SBEvent &event) {
525   return event.GetBroadcasterClass() ==
526          SBCommandInterpreter::GetBroadcasterClass();
527 }
528 
529 bool SBCommandInterpreter::SetCommandOverrideCallback(
530     const char *command_name, lldb::CommandOverrideCallback callback,
531     void *baton) {
532   if (command_name && command_name[0] && IsValid()) {
533     llvm::StringRef command_name_str = command_name;
534     CommandObject *cmd_obj =
535         m_opaque_ptr->GetCommandObjectForCommand(command_name_str);
536     if (cmd_obj) {
537       assert(command_name_str.empty());
538       cmd_obj->SetOverrideCallback(callback, baton);
539       return true;
540     }
541   }
542   return false;
543 }
544 
545 lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name,
546                                                           const char *help) {
547   CommandObjectMultiword *new_command =
548       new CommandObjectMultiword(*m_opaque_ptr, name, help);
549   new_command->SetRemovable(true);
550   lldb::CommandObjectSP new_command_sp(new_command);
551   if (new_command_sp &&
552       m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
553     return lldb::SBCommand(new_command_sp);
554   return lldb::SBCommand();
555 }
556 
557 lldb::SBCommand SBCommandInterpreter::AddCommand(
558     const char *name, lldb::SBCommandPluginInterface *impl, const char *help) {
559   lldb::CommandObjectSP new_command_sp;
560   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
561       *m_opaque_ptr, name, impl, help);
562 
563   if (new_command_sp &&
564       m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
565     return lldb::SBCommand(new_command_sp);
566   return lldb::SBCommand();
567 }
568 
569 lldb::SBCommand
570 SBCommandInterpreter::AddCommand(const char *name,
571                                  lldb::SBCommandPluginInterface *impl,
572                                  const char *help, const char *syntax) {
573   lldb::CommandObjectSP new_command_sp;
574   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
575       *m_opaque_ptr, name, impl, help, syntax);
576 
577   if (new_command_sp &&
578       m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
579     return lldb::SBCommand(new_command_sp);
580   return lldb::SBCommand();
581 }
582 
583 SBCommand::SBCommand() = default;
584 
585 SBCommand::SBCommand(lldb::CommandObjectSP cmd_sp) : m_opaque_sp(cmd_sp) {}
586 
587 bool SBCommand::IsValid() { return m_opaque_sp.get() != nullptr; }
588 
589 const char *SBCommand::GetName() {
590   return (IsValid() ? ConstString(m_opaque_sp->GetCommandName()).AsCString() : nullptr);
591 }
592 
593 const char *SBCommand::GetHelp() {
594   return (IsValid() ? ConstString(m_opaque_sp->GetHelp()).AsCString()
595                     : nullptr);
596 }
597 
598 const char *SBCommand::GetHelpLong() {
599   return (IsValid() ? ConstString(m_opaque_sp->GetHelpLong()).AsCString()
600                     : nullptr);
601 }
602 
603 void SBCommand::SetHelp(const char *help) {
604   if (IsValid())
605     m_opaque_sp->SetHelp(help);
606 }
607 
608 void SBCommand::SetHelpLong(const char *help) {
609   if (IsValid())
610     m_opaque_sp->SetHelpLong(help);
611 }
612 
613 lldb::SBCommand SBCommand::AddMultiwordCommand(const char *name,
614                                                const char *help) {
615   if (!IsValid())
616     return lldb::SBCommand();
617   if (!m_opaque_sp->IsMultiwordObject())
618     return lldb::SBCommand();
619   CommandObjectMultiword *new_command = new CommandObjectMultiword(
620       m_opaque_sp->GetCommandInterpreter(), name, help);
621   new_command->SetRemovable(true);
622   lldb::CommandObjectSP new_command_sp(new_command);
623   if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
624     return lldb::SBCommand(new_command_sp);
625   return lldb::SBCommand();
626 }
627 
628 lldb::SBCommand SBCommand::AddCommand(const char *name,
629                                       lldb::SBCommandPluginInterface *impl,
630                                       const char *help) {
631   if (!IsValid())
632     return lldb::SBCommand();
633   if (!m_opaque_sp->IsMultiwordObject())
634     return lldb::SBCommand();
635   lldb::CommandObjectSP new_command_sp;
636   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
637       m_opaque_sp->GetCommandInterpreter(), name, impl, help);
638   if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
639     return lldb::SBCommand(new_command_sp);
640   return lldb::SBCommand();
641 }
642 
643 lldb::SBCommand SBCommand::AddCommand(const char *name,
644                                       lldb::SBCommandPluginInterface *impl,
645                                       const char *help, const char *syntax) {
646   if (!IsValid())
647     return lldb::SBCommand();
648   if (!m_opaque_sp->IsMultiwordObject())
649     return lldb::SBCommand();
650   lldb::CommandObjectSP new_command_sp;
651   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
652       m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax);
653   if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
654     return lldb::SBCommand(new_command_sp);
655   return lldb::SBCommand();
656 }
657 
658 uint32_t SBCommand::GetFlags() {
659   return (IsValid() ? m_opaque_sp->GetFlags().Get() : 0);
660 }
661 
662 void SBCommand::SetFlags(uint32_t flags) {
663   if (IsValid())
664     m_opaque_sp->GetFlags().Set(flags);
665 }
666