CommandObjectSource.cpp revision 781241147dbdd18762ab676960ecbff18ab0a766
1//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "lldb/lldb-python.h" 11 12#include "CommandObjectSource.h" 13 14// C Includes 15// C++ Includes 16// Other libraries and framework includes 17// Project includes 18#include "lldb/Interpreter/Args.h" 19#include "lldb/Core/Debugger.h" 20#include "lldb/Core/FileLineResolver.h" 21#include "lldb/Core/Module.h" 22#include "lldb/Core/ModuleSpec.h" 23#include "lldb/Core/SourceManager.h" 24#include "lldb/Interpreter/CommandInterpreter.h" 25#include "lldb/Interpreter/CommandReturnObject.h" 26#include "lldb/Host/FileSpec.h" 27#include "lldb/Symbol/CompileUnit.h" 28#include "lldb/Symbol/Function.h" 29#include "lldb/Target/Process.h" 30#include "lldb/Target/TargetList.h" 31#include "lldb/Interpreter/CommandCompletions.h" 32#include "lldb/Interpreter/Options.h" 33 34using namespace lldb; 35using namespace lldb_private; 36 37//------------------------------------------------------------------------- 38// CommandObjectSourceInfo 39//------------------------------------------------------------------------- 40 41class CommandObjectSourceInfo : public CommandObjectParsed 42{ 43 44 class CommandOptions : public Options 45 { 46 public: 47 CommandOptions (CommandInterpreter &interpreter) : 48 Options(interpreter) 49 { 50 } 51 52 ~CommandOptions () 53 { 54 } 55 56 Error 57 SetOptionValue (uint32_t option_idx, const char *option_arg) 58 { 59 Error error; 60 const int short_option = g_option_table[option_idx].short_option; 61 switch (short_option) 62 { 63 case 'l': 64 start_line = Args::StringToUInt32 (option_arg, 0); 65 if (start_line == 0) 66 error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); 67 break; 68 69 case 'f': 70 file_name = option_arg; 71 break; 72 73 default: 74 error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); 75 break; 76 } 77 78 return error; 79 } 80 81 void 82 OptionParsingStarting () 83 { 84 file_spec.Clear(); 85 file_name.clear(); 86 start_line = 0; 87 } 88 89 const OptionDefinition* 90 GetDefinitions () 91 { 92 return g_option_table; 93 } 94 static OptionDefinition g_option_table[]; 95 96 // Instance variables to hold the values for command options. 97 FileSpec file_spec; 98 std::string file_name; 99 uint32_t start_line; 100 101 }; 102 103public: 104 CommandObjectSourceInfo(CommandInterpreter &interpreter) : 105 CommandObjectParsed (interpreter, 106 "source info", 107 "Display information about the source lines from the current executable's debug info.", 108 "source info [<cmd-options>]"), 109 m_options (interpreter) 110 { 111 } 112 113 ~CommandObjectSourceInfo () 114 { 115 } 116 117 118 Options * 119 GetOptions () 120 { 121 return &m_options; 122 } 123 124protected: 125 bool 126 DoExecute (Args& command, CommandReturnObject &result) 127 { 128 result.AppendError ("Not yet implemented"); 129 result.SetStatus (eReturnStatusFailed); 130 return false; 131 } 132 133 CommandOptions m_options; 134}; 135 136OptionDefinition 137CommandObjectSourceInfo::CommandOptions::g_option_table[] = 138{ 139{ LLDB_OPT_SET_1, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, 140{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, 141{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 142}; 143 144#pragma mark CommandObjectSourceList 145//------------------------------------------------------------------------- 146// CommandObjectSourceList 147//------------------------------------------------------------------------- 148 149class CommandObjectSourceList : public CommandObjectParsed 150{ 151 152 class CommandOptions : public Options 153 { 154 public: 155 CommandOptions (CommandInterpreter &interpreter) : 156 Options(interpreter) 157 { 158 } 159 160 ~CommandOptions () 161 { 162 } 163 164 Error 165 SetOptionValue (uint32_t option_idx, const char *option_arg) 166 { 167 Error error; 168 const int short_option = g_option_table[option_idx].short_option; 169 switch (short_option) 170 { 171 case 'l': 172 start_line = Args::StringToUInt32 (option_arg, 0); 173 if (start_line == 0) 174 error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); 175 break; 176 177 case 'c': 178 num_lines = Args::StringToUInt32 (option_arg, 0); 179 if (num_lines == 0) 180 error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); 181 break; 182 183 case 'f': 184 file_name = option_arg; 185 break; 186 187 case 'n': 188 symbol_name = option_arg; 189 break; 190 191 case 'a': 192 { 193 ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); 194 address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); 195 } 196 break; 197 case 's': 198 modules.push_back (std::string (option_arg)); 199 break; 200 201 case 'b': 202 show_bp_locs = true; 203 break; 204 default: 205 error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); 206 break; 207 } 208 209 return error; 210 } 211 212 void 213 OptionParsingStarting () 214 { 215 file_spec.Clear(); 216 file_name.clear(); 217 symbol_name.clear(); 218 address = LLDB_INVALID_ADDRESS; 219 start_line = 0; 220 num_lines = 10; 221 show_bp_locs = false; 222 modules.clear(); 223 } 224 225 const OptionDefinition* 226 GetDefinitions () 227 { 228 return g_option_table; 229 } 230 static OptionDefinition g_option_table[]; 231 232 // Instance variables to hold the values for command options. 233 FileSpec file_spec; 234 std::string file_name; 235 std::string symbol_name; 236 lldb::addr_t address; 237 uint32_t start_line; 238 uint32_t num_lines; 239 STLStringArray modules; 240 bool show_bp_locs; 241 }; 242 243public: 244 CommandObjectSourceList(CommandInterpreter &interpreter) : 245 CommandObjectParsed (interpreter, 246 "source list", 247 "Display source code (as specified) based on the current executable's debug info.", 248 NULL), 249 m_options (interpreter) 250 { 251 CommandArgumentEntry arg; 252 CommandArgumentData file_arg; 253 254 // Define the first (and only) variant of this arg. 255 file_arg.arg_type = eArgTypeFilename; 256 file_arg.arg_repetition = eArgRepeatOptional; 257 258 // There is only one variant this argument could be; put it into the argument entry. 259 arg.push_back (file_arg); 260 261 // Push the data for the first argument into the m_arguments vector. 262 m_arguments.push_back (arg); 263 } 264 265 ~CommandObjectSourceList () 266 { 267 } 268 269 270 Options * 271 GetOptions () 272 { 273 return &m_options; 274 } 275 276 virtual const char * 277 GetRepeatCommand (Args ¤t_command_args, uint32_t index) 278 { 279 return m_cmd_name.c_str(); 280 } 281 282protected: 283 bool 284 DoExecute (Args& command, CommandReturnObject &result) 285 { 286 const int argc = command.GetArgumentCount(); 287 288 if (argc != 0) 289 { 290 result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); 291 result.SetStatus (eReturnStatusFailed); 292 return false; 293 } 294 295 ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); 296 Target *target = exe_ctx.GetTargetPtr(); 297 298 if (target == NULL) 299 target = m_interpreter.GetDebugger().GetSelectedTarget().get(); 300 301 if (target == NULL) 302 { 303 result.AppendError ("invalid target, create a debug target using the 'target create' command"); 304 result.SetStatus (eReturnStatusFailed); 305 return false; 306 } 307 308 SymbolContextList sc_list; 309 if (!m_options.symbol_name.empty()) 310 { 311 // Displaying the source for a symbol: 312 ConstString name(m_options.symbol_name.c_str()); 313 bool include_symbols = false; 314 bool include_inlines = true; 315 bool append = true; 316 size_t num_matches = 0; 317 318 if (m_options.modules.size() > 0) 319 { 320 ModuleList matching_modules; 321 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++) 322 { 323 FileSpec module_file_spec(m_options.modules[i].c_str(), false); 324 if (module_file_spec) 325 { 326 ModuleSpec module_spec (module_file_spec); 327 matching_modules.Clear(); 328 target->GetImages().FindModules (module_spec, matching_modules); 329 num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); 330 } 331 } 332 } 333 else 334 { 335 num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); 336 } 337 338 SymbolContext sc; 339 340 if (num_matches == 0) 341 { 342 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str()); 343 result.SetStatus (eReturnStatusFailed); 344 return false; 345 } 346 347 sc_list.GetContextAtIndex (0, sc); 348 FileSpec start_file; 349 uint32_t start_line; 350 uint32_t end_line; 351 FileSpec end_file; 352 if (sc.function != NULL) 353 { 354 sc.function->GetStartLineSourceInfo (start_file, start_line); 355 if (start_line == 0) 356 { 357 result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str()); 358 result.SetStatus (eReturnStatusFailed); 359 return false; 360 } 361 sc.function->GetEndLineSourceInfo (end_file, end_line); 362 } 363 else 364 { 365 result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); 366 result.SetStatus (eReturnStatusFailed); 367 return false; 368 } 369 370 if (num_matches > 1) 371 { 372 // This could either be because there are multiple functions of this name, in which case 373 // we'll have to specify this further... Or it could be because there are multiple inlined instances 374 // of one function. So run through the matches and if they all have the same file & line then we can just 375 // list one. 376 377 bool found_multiple = false; 378 379 for (size_t i = 1; i < num_matches; i++) 380 { 381 SymbolContext scratch_sc; 382 sc_list.GetContextAtIndex (i, scratch_sc); 383 if (scratch_sc.function != NULL) 384 { 385 FileSpec scratch_file; 386 uint32_t scratch_line; 387 scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line); 388 if (scratch_file != start_file 389 || scratch_line != start_line) 390 { 391 found_multiple = true; 392 break; 393 } 394 } 395 } 396 if (found_multiple) 397 { 398 StreamString s; 399 for (size_t i = 0; i < num_matches; i++) 400 { 401 SymbolContext scratch_sc; 402 sc_list.GetContextAtIndex (i, scratch_sc); 403 if (scratch_sc.function != NULL) 404 { 405 s.Printf("\n%lu: ", i); 406 scratch_sc.function->Dump (&s, true); 407 } 408 } 409 result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n", 410 m_options.symbol_name.c_str(), 411 s.GetData()); 412 result.SetStatus (eReturnStatusFailed); 413 return false; 414 } 415 } 416 417 418 // This is a little hacky, but the first line table entry for a function points to the "{" that 419 // starts the function block. It would be nice to actually get the function 420 // declaration in there too. So back up a bit, but not further than what you're going to display. 421 size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2; 422 uint32_t line_no; 423 if (start_line <= lines_to_back_up) 424 line_no = 1; 425 else 426 line_no = start_line - lines_to_back_up; 427 428 // For fun, if the function is shorter than the number of lines we're supposed to display, 429 // only display the function... 430 if (end_line != 0) 431 { 432 if (m_options.num_lines > end_line - line_no) 433 m_options.num_lines = end_line - line_no; 434 } 435 436 char path_buf[PATH_MAX]; 437 start_file.GetPath(path_buf, sizeof(path_buf)); 438 439 if (m_options.show_bp_locs) 440 { 441 const bool show_inlines = true; 442 m_breakpoint_locations.Reset (start_file, 0, show_inlines); 443 SearchFilter target_search_filter (exe_ctx.GetTargetSP()); 444 target_search_filter.Search (m_breakpoint_locations); 445 } 446 else 447 m_breakpoint_locations.Clear(); 448 449 result.AppendMessageWithFormat("File: %s.\n", path_buf); 450 target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, 451 line_no, 452 0, 453 m_options.num_lines, 454 "", 455 &result.GetOutputStream(), 456 GetBreakpointLocations ()); 457 458 result.SetStatus (eReturnStatusSuccessFinishResult); 459 return true; 460 461 } 462 else if (m_options.address != LLDB_INVALID_ADDRESS) 463 { 464 SymbolContext sc; 465 Address so_addr; 466 StreamString error_strm; 467 468 if (target->GetSectionLoadList().IsEmpty()) 469 { 470 // The target isn't loaded yet, we need to lookup the file address 471 // in all modules 472 const ModuleList &module_list = target->GetImages(); 473 const uint32_t num_modules = module_list.GetSize(); 474 for (uint32_t i=0; i<num_modules; ++i) 475 { 476 ModuleSP module_sp (module_list.GetModuleAtIndex(i)); 477 if (module_sp && module_sp->ResolveFileAddress(m_options.address, so_addr)) 478 { 479 sc.Clear(); 480 if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) 481 sc_list.Append(sc); 482 } 483 } 484 485 if (sc_list.GetSize() == 0) 486 { 487 result.AppendErrorWithFormat("no modules have source information for file address 0x%" PRIx64 ".\n", 488 m_options.address); 489 result.SetStatus (eReturnStatusFailed); 490 return false; 491 } 492 } 493 else 494 { 495 // The target has some things loaded, resolve this address to a 496 // compile unit + file + line and display 497 if (target->GetSectionLoadList().ResolveLoadAddress (m_options.address, so_addr)) 498 { 499 ModuleSP module_sp (so_addr.GetModule()); 500 if (module_sp) 501 { 502 sc.Clear(); 503 if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) 504 { 505 sc_list.Append(sc); 506 } 507 else 508 { 509 so_addr.Dump(&error_strm, NULL, Address::DumpStyleModuleWithFileAddress); 510 result.AppendErrorWithFormat("address resolves to %s, but there is no line table information available for this address.\n", 511 error_strm.GetData()); 512 result.SetStatus (eReturnStatusFailed); 513 return false; 514 } 515 } 516 } 517 518 if (sc_list.GetSize() == 0) 519 { 520 result.AppendErrorWithFormat("no modules contain load address 0x%" PRIx64 ".\n", m_options.address); 521 result.SetStatus (eReturnStatusFailed); 522 return false; 523 } 524 } 525 uint32_t num_matches = sc_list.GetSize(); 526 for (uint32_t i=0; i<num_matches; ++i) 527 { 528 sc_list.GetContextAtIndex(i, sc); 529 if (sc.comp_unit) 530 { 531 if (m_options.show_bp_locs) 532 { 533 m_breakpoint_locations.Clear(); 534 const bool show_inlines = true; 535 m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); 536 SearchFilter target_search_filter (target->shared_from_this()); 537 target_search_filter.Search (m_breakpoint_locations); 538 } 539 540 bool show_fullpaths = true; 541 bool show_module = true; 542 bool show_inlined_frames = true; 543 sc.DumpStopContext(&result.GetOutputStream(), 544 exe_ctx.GetBestExecutionContextScope(), 545 sc.line_entry.range.GetBaseAddress(), 546 show_fullpaths, 547 show_module, 548 show_inlined_frames); 549 result.GetOutputStream().EOL(); 550 551 size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2; 552 553 target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, 554 sc.line_entry.line, 555 lines_to_back_up, 556 m_options.num_lines - lines_to_back_up, 557 "->", 558 &result.GetOutputStream(), 559 GetBreakpointLocations ()); 560 result.SetStatus (eReturnStatusSuccessFinishResult); 561 } 562 } 563 } 564 else if (m_options.file_name.empty()) 565 { 566 // Last valid source manager context, or the current frame if no 567 // valid last context in source manager. 568 // One little trick here, if you type the exact same list command twice in a row, it is 569 // more likely because you typed it once, then typed it again 570 if (m_options.start_line == 0) 571 { 572 if (target->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(), 573 GetBreakpointLocations ())) 574 { 575 result.SetStatus (eReturnStatusSuccessFinishResult); 576 } 577 } 578 else 579 { 580 if (m_options.show_bp_locs) 581 { 582 SourceManager::FileSP last_file_sp (target->GetSourceManager().GetLastFile ()); 583 if (last_file_sp) 584 { 585 const bool show_inlines = true; 586 m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines); 587 SearchFilter target_search_filter (target->shared_from_this()); 588 target_search_filter.Search (m_breakpoint_locations); 589 } 590 } 591 else 592 m_breakpoint_locations.Clear(); 593 594 if (target->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile( 595 m_options.start_line, // Line to display 596 0, // Lines before line to display 597 m_options.num_lines, // Lines after line to display 598 "", // Don't mark "line" 599 &result.GetOutputStream(), 600 GetBreakpointLocations ())) 601 { 602 result.SetStatus (eReturnStatusSuccessFinishResult); 603 } 604 605 } 606 } 607 else 608 { 609 const char *filename = m_options.file_name.c_str(); 610 611 bool check_inlines = false; 612 SymbolContextList sc_list; 613 size_t num_matches = 0; 614 615 if (m_options.modules.size() > 0) 616 { 617 ModuleList matching_modules; 618 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++) 619 { 620 FileSpec module_file_spec(m_options.modules[i].c_str(), false); 621 if (module_file_spec) 622 { 623 ModuleSpec module_spec (module_file_spec); 624 matching_modules.Clear(); 625 target->GetImages().FindModules (module_spec, matching_modules); 626 num_matches += matching_modules.ResolveSymbolContextForFilePath (filename, 627 0, 628 check_inlines, 629 eSymbolContextModule | eSymbolContextCompUnit, 630 sc_list); 631 } 632 } 633 } 634 else 635 { 636 num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename, 637 0, 638 check_inlines, 639 eSymbolContextModule | eSymbolContextCompUnit, 640 sc_list); 641 } 642 643 if (num_matches == 0) 644 { 645 result.AppendErrorWithFormat("Could not find source file \"%s\".\n", 646 m_options.file_name.c_str()); 647 result.SetStatus (eReturnStatusFailed); 648 return false; 649 } 650 651 if (num_matches > 1) 652 { 653 SymbolContext sc; 654 bool got_multiple = false; 655 FileSpec *test_cu_spec = NULL; 656 657 for (unsigned i = 0; i < num_matches; i++) 658 { 659 sc_list.GetContextAtIndex(i, sc); 660 if (sc.comp_unit) 661 { 662 if (test_cu_spec) 663 { 664 if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit)) 665 got_multiple = true; 666 break; 667 } 668 else 669 test_cu_spec = sc.comp_unit; 670 } 671 } 672 if (got_multiple) 673 { 674 result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n", 675 m_options.file_name.c_str()); 676 result.SetStatus (eReturnStatusFailed); 677 return false; 678 } 679 } 680 681 SymbolContext sc; 682 if (sc_list.GetContextAtIndex(0, sc)) 683 { 684 if (sc.comp_unit) 685 { 686 if (m_options.show_bp_locs) 687 { 688 const bool show_inlines = true; 689 m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); 690 SearchFilter target_search_filter (target->shared_from_this()); 691 target_search_filter.Search (m_breakpoint_locations); 692 } 693 else 694 m_breakpoint_locations.Clear(); 695 696 target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, 697 m_options.start_line, 698 0, 699 m_options.num_lines, 700 "", 701 &result.GetOutputStream(), 702 GetBreakpointLocations ()); 703 704 result.SetStatus (eReturnStatusSuccessFinishResult); 705 } 706 else 707 { 708 result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", 709 m_options.file_name.c_str()); 710 result.SetStatus (eReturnStatusFailed); 711 return false; 712 } 713 } 714 } 715 return result.Succeeded(); 716 } 717 718 const SymbolContextList * 719 GetBreakpointLocations () 720 { 721 if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) 722 return &m_breakpoint_locations.GetFileLineMatches(); 723 return NULL; 724 } 725 CommandOptions m_options; 726 FileLineResolver m_breakpoint_locations; 727 728}; 729 730OptionDefinition 731CommandObjectSourceList::CommandOptions::g_option_table[] = 732{ 733{ LLDB_OPT_SET_ALL, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "The number of source lines to display."}, 734{ LLDB_OPT_SET_1 | 735 LLDB_OPT_SET_2 , false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."}, 736{ LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."}, 737{ LLDB_OPT_SET_1 , false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, 738{ LLDB_OPT_SET_1 , false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, 739{ LLDB_OPT_SET_2 , false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, 740{ LLDB_OPT_SET_3 , false, "address",'a', required_argument, NULL, 0, eArgTypeAddress, "Lookup the address and display the source information for the corresponding file and line."}, 741{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 742}; 743 744#pragma mark CommandObjectMultiwordSource 745 746//------------------------------------------------------------------------- 747// CommandObjectMultiwordSource 748//------------------------------------------------------------------------- 749 750CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) : 751 CommandObjectMultiword (interpreter, 752 "source", 753 "A set of commands for accessing source file information", 754 "source <subcommand> [<subcommand-options>]") 755{ 756 LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); 757 LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); 758} 759 760CommandObjectMultiwordSource::~CommandObjectMultiwordSource () 761{ 762} 763 764