diff options
Diffstat (limited to 'lldb/source/Interpreter')
-rw-r--r-- | lldb/source/Interpreter/CommandCompletions.cpp | 414 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandContext.cpp | 77 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandInterpreter.cpp | 1300 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObject.cpp | 448 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObjectCrossref.cpp | 92 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObjectMultiword.cpp | 263 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObjectRegexCommand.cpp | 123 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandReturnObject.cpp | 175 | ||||
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreter.cpp | 66 | ||||
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreterNone.cpp | 38 | ||||
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreterPython.cpp | 830 | ||||
-rw-r--r-- | lldb/source/Interpreter/StateVariable.cpp | 320 | ||||
-rw-r--r-- | lldb/source/Interpreter/embedded_interpreter.py | 90 |
13 files changed, 4236 insertions, 0 deletions
diff --git a/lldb/source/Interpreter/CommandCompletions.cpp b/lldb/source/Interpreter/CommandCompletions.cpp new file mode 100644 index 00000000000..a299ffb098f --- /dev/null +++ b/lldb/source/Interpreter/CommandCompletions.cpp @@ -0,0 +1,414 @@ +//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandCompletions.h" + + +using namespace lldb_private; + +CommandCompletions::CommonCompletionElement +CommandCompletions::g_common_completions[] = +{ + {eCustomCompletion, NULL}, + {eSourceFileCompletion, CommandCompletions::SourceFiles}, + {eDiskFileCompletion, NULL}, + {eSymbolCompletion, CommandCompletions::Symbols}, + {eModuleCompletion, CommandCompletions::Modules}, + {eNoCompletion, NULL} // This one has to be last in the list. +}; + +bool +CommandCompletions::InvokeCommonCompletionCallbacks (uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + bool handled = false; + + if (completion_mask & eCustomCompletion) + return false; + + for (int i = 0; ; i++) + { + if (g_common_completions[i].type == eNoCompletion) + break; + else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type + && g_common_completions[i].callback != NULL) + { + handled = true; + g_common_completions[i].callback (completion_str, + match_start_point, + max_return_elements, + interpreter, + searcher, + matches); + } + } + return handled; +} + +int +CommandCompletions::SourceFiles (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + // Find some way to switch "include support files..." + SourceFileCompleter completer (false, partial_file_name, match_start_point, max_return_elements, interpreter, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Modules (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + ModuleCompleter completer(partial_file_name, match_start_point, max_return_elements, interpreter, matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Symbols (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + SymbolCompleter completer(partial_file_name, match_start_point, max_return_elements, interpreter, matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +CommandCompletions::Completer::Completer ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + m_completion_str (completion_str), + m_match_start_point (match_start_point), + m_max_return_elements (max_return_elements), + m_interpreter (interpreter), + m_matches (matches) +{ +} + +CommandCompletions::Completer::~Completer () +{ + +} + +//---------------------------------------------------------------------- +// SourceFileCompleter +//---------------------------------------------------------------------- + +CommandCompletions::SourceFileCompleter::SourceFileCompleter ( + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches), + m_include_support_files (include_support_files), + m_matching_files() +{ + FileSpec partial_spec (m_completion_str.c_str()); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::SourceFileCompleter::GetDepth() +{ + return eDepthCompUnit; +} + +Searcher::CallbackReturn +CommandCompletions::SourceFileCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.comp_unit != NULL) + { + if (m_include_support_files) + { + FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); + for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) + { + const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles); + const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); + const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); + bool match = false; + if (m_file_name && sfile_file_name + && strstr (sfile_file_name, m_file_name) == sfile_file_name) + match = true; + if (match && m_dir_name && sfile_dir_name + && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name) + match = false; + + if (match) + { + m_matching_files.AppendIfUnique(sfile_spec); + } + } + + } + else + { + const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); + const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name + && strstr (cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name + && strstr (cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) + { + m_matching_files.AppendIfUnique(context.comp_unit); + } + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + // Now convert the filelist to completions: + for (size_t i = 0; i < m_matching_files.GetSize(); i++) + { + m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); + } + return m_matches.GetSize(); + +} + +//---------------------------------------------------------------------- +// SymbolCompleter +//---------------------------------------------------------------------- + +static bool +regex_chars (const char comp) +{ + if (comp == '[' || comp == ']' || comp == '(' || comp == ')') + return true; + else + return false; +} +CommandCompletions::SymbolCompleter::SymbolCompleter ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches) +{ + std::string regex_str ("^"); + regex_str.append(completion_str); + regex_str.append(".*"); + std::string::iterator pos; + + pos = find_if(regex_str.begin(), regex_str.end(), regex_chars); + while (pos < regex_str.end()) { + pos = regex_str.insert(pos, '\\'); + pos += 2; + pos = find_if(pos, regex_str.end(), regex_chars); + } + m_regex.Compile(regex_str.c_str()); +} + +Searcher::Depth +CommandCompletions::SymbolCompleter::GetDepth() +{ + return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::SymbolCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + SymbolContextList func_list; + SymbolContextList sym_list; + + if (context.module_sp != NULL) + { + if (context.module_sp) + { + context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, lldb::eSymbolTypeCode, sym_list); + context.module_sp->FindFunctions (m_regex, true, func_list); + } + + SymbolContext sc; + // Now add the functions & symbols to the list - only add if unique: + for (int i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc)) + { + if (sc.function) + { + m_match_set.insert (sc.function->GetMangled().GetDemangledName()); + } + } + } + + for (int i = 0; i < sym_list.GetSize(); i++) + { + if (sym_list.GetContextAtIndex(i, sc)) + { + if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + m_match_set.insert (sc.symbol->GetMangled().GetDemangledName()); + } + } + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); + for (pos = m_match_set.begin(); pos != end; pos++) + m_matches.AppendString((*pos).GetCString()); + + return m_matches.GetSize(); +} + +//---------------------------------------------------------------------- +// ModuleCompleter +//---------------------------------------------------------------------- +CommandCompletions::ModuleCompleter::ModuleCompleter ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches) +{ + FileSpec partial_spec (m_completion_str.c_str()); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::ModuleCompleter::GetDepth() +{ + return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::ModuleCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.module_sp != NULL) + { + const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString(); + const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name + && strstr (cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name + && strstr (cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) + { + m_matches.AppendString (cur_file_name); + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + return m_matches.GetSize(); +} + + + diff --git a/lldb/source/Interpreter/CommandContext.cpp b/lldb/source/Interpreter/CommandContext.cpp new file mode 100644 index 00000000000..012611c5262 --- /dev/null +++ b/lldb/source/Interpreter/CommandContext.cpp @@ -0,0 +1,77 @@ +//===-- CommandContext.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandContext.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +CommandContext::CommandContext () : + m_exe_ctx () +{ +} + +CommandContext::~CommandContext () +{ +} + +Target * +CommandContext::GetTarget() +{ + return Debugger::GetSharedInstance().GetCurrentTarget().get(); +} + + +ExecutionContext & +CommandContext::GetExecutionContext() +{ + return m_exe_ctx; +} + +void +CommandContext::Update (ExecutionContext *override_context) +{ + m_exe_ctx.Clear(); + + if (override_context != NULL) + { + m_exe_ctx.target = override_context->target; + m_exe_ctx.process = override_context->process; + m_exe_ctx.thread = override_context->thread; + m_exe_ctx.frame = override_context->frame; + } + else + { + TargetSP target_sp (Debugger::GetSharedInstance().GetCurrentTarget()); + if (target_sp) + { + m_exe_ctx.process = target_sp->GetProcessSP().get(); + if (m_exe_ctx.process && m_exe_ctx.process->IsRunning() == false) + { + m_exe_ctx.thread = m_exe_ctx.process->GetThreadList().GetCurrentThread().get(); + if (m_exe_ctx.thread == NULL) + m_exe_ctx.thread = m_exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (m_exe_ctx.thread) + { + m_exe_ctx.frame = m_exe_ctx.thread->GetCurrentFrame().get(); + if (m_exe_ctx.frame == NULL) + m_exe_ctx.frame = m_exe_ctx.thread->GetStackFrameAtIndex (0).get(); + } + } + } + } +} diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp new file mode 100644 index 00000000000..ed85b33bab3 --- /dev/null +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -0,0 +1,1300 @@ +//===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string> + +#include <getopt.h> +#include <stdlib.h> + +#include "CommandObjectAdd.h" +#include "CommandObjectAlias.h" +#include "CommandObjectAppend.h" +#include "CommandObjectApropos.h" +#include "CommandObjectArgs.h" +#include "CommandObjectBreakpoint.h" +#include "CommandObjectCall.h" +#include "CommandObjectDelete.h" +#include "CommandObjectDisassemble.h" +#include "CommandObjectExpression.h" +#include "CommandObjectFile.h" +#include "CommandObjectFrame.h" +#include "CommandObjectHelp.h" +#include "CommandObjectImage.h" +#include "CommandObjectInfo.h" +#include "CommandObjectLog.h" +#include "CommandObjectMemory.h" +#include "CommandObjectProcess.h" +#include "CommandObjectQuit.h" +#include "CommandObjectRegexCommand.h" +#include "CommandObjectRegister.h" +#include "CommandObjectRemove.h" +#include "CommandObjectScript.h" +#include "CommandObjectSelect.h" +#include "CommandObjectSet.h" +#include "CommandObjectSettings.h" +#include "CommandObjectShow.h" +#include "CommandObjectSource.h" +#include "CommandObjectSourceFile.h" +#include "CommandObjectStatus.h" +#include "CommandObjectSyntax.h" +#include "CommandObjectTarget.h" +#include "CommandObjectThread.h" +#include "CommandObjectTranslate.h" +#include "CommandObjectUnalias.h" +#include "CommandObjectVariable.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +CommandInterpreter::CommandInterpreter +( + ScriptLanguage script_language, + bool synchronous_execution, + Listener *listener, + SourceManager& source_manager +) : + Broadcaster ("CommandInterpreter"), + m_script_language (script_language), + m_synchronous_execution (synchronous_execution), + m_listener (listener), + m_source_manager (source_manager) +{ +} + +void +CommandInterpreter::Initialize () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + CommandReturnObject result; + + LoadCommandDictionary (); + + InitializeVariables (); + + // Set up some initial aliases. + result.Clear(); HandleCommand ("alias q quit", false, result); + result.Clear(); HandleCommand ("alias run process launch", false, result); + result.Clear(); HandleCommand ("alias r process launch", false, result); + result.Clear(); HandleCommand ("alias c process continue", false, result); + result.Clear(); HandleCommand ("alias continue process continue", false, result); + result.Clear(); HandleCommand ("alias expr expression", false, result); + result.Clear(); HandleCommand ("alias exit quit", false, result); + result.Clear(); HandleCommand ("alias bt thread backtrace", false, result); + result.Clear(); HandleCommand ("alias si thread step-inst", false, result); + result.Clear(); HandleCommand ("alias step thread step-in", false, result); + result.Clear(); HandleCommand ("alias s thread step-in", false, result); + result.Clear(); HandleCommand ("alias next thread step-over", false, result); + result.Clear(); HandleCommand ("alias n thread step-over", false, result); + result.Clear(); HandleCommand ("alias finish thread step-out", false, result); + result.Clear(); HandleCommand ("alias x memory read", false, result); + result.Clear(); HandleCommand ("alias l source-file", false, result); + result.Clear(); HandleCommand ("alias list source-file", false, result); +} + +void +CommandInterpreter::InitializeVariables () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + m_variables["prompt"] = + StateVariableSP (new StateVariable ("prompt", + "(lldb) ", + false, + "The debugger prompt displayed for the user.", + StateVariable::BroadcastPromptChange)); + + m_variables["run-args"] = + StateVariableSP (new StateVariable ("run-args", + (Args*)NULL, + "An argument list containing the arguments to be passed to the executable when it is launched.")); + + + m_variables["env-vars"] = + StateVariableSP (new StateVariable ("env-vars", + (Args*)NULL, + "A list of strings containing the environment variables to be passed to the executable's environment.")); + + m_variables["input-path"] = + StateVariableSP (new StateVariable ("input-path", + "/dev/stdin", + false, + "The file/path to be used by the executable program for reading its input.")); + + m_variables["output-path"] = + StateVariableSP (new StateVariable ( "output-path", + "/dev/stdout", + false, + "The file/path to be used by the executable program for writing its output.")); + + m_variables["error-path"] = + StateVariableSP (new StateVariable ("error-path", + "/dev/stderr", + false, + "The file/path to be used by the executable program for writing its error messages.")); + + m_variables["arch"] = + StateVariableSP (new StateVariable ("arch", + "", + false, + "The architecture to be used for running the executable (e.g. i386, x86_64, etc).")); + + m_variables["script-lang"] = + StateVariableSP (new StateVariable ("script-lang", + "Python", + false, + "The script language to be used for evaluating user-written scripts.", + StateVariable::VerifyScriptLanguage)); + + m_variables["term-width"] = + StateVariableSP (new StateVariable ("term-width", + 80, + "The maximum number of columns to use for displaying text.")); + +} + +const char * +CommandInterpreter::ProcessEmbeddedScriptCommands (const char *arg) +{ + // This function has not yet been implemented. + + // Look for any embedded script command + // If found, + // get interpreter object from the command dictionary, + // call execute_one_command on it, + // get the results as a string, + // substitute that string for current stuff. + + return arg; +} + + +void +CommandInterpreter::LoadCommandDictionary () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + // **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT *** + // + // Command objects that are used as cross reference objects (i.e. they inherit from CommandObjectCrossref) + // *MUST* be created and put into the command dictionary *BEFORE* any multi-word commands (which may use + // the cross-referencing stuff) are created!!! + // + // **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT *** + + + // Command objects that inherit from CommandObjectCrossref must be created before other command objects + // are created. This is so that when another command is created that needs to go into a crossref object, + // the crossref object exists and is ready to take the cross reference. Put the cross referencing command + // objects into the CommandDictionary now, so they are ready for use when the other commands get created. + + m_command_dict["select"] = CommandObjectSP (new CommandObjectSelect ()); + m_command_dict["info"] = CommandObjectSP (new CommandObjectInfo ()); + m_command_dict["delete"] = CommandObjectSP (new CommandObjectDelete ()); + + // Non-CommandObjectCrossref commands can now be created. + + //m_command_dict["add"] = CommandObjectSP (new CommandObjectAdd ()); + m_command_dict["alias"] = CommandObjectSP (new CommandObjectAlias ()); + m_command_dict["append"] = CommandObjectSP (new CommandObjectAppend ()); + m_command_dict["apropos"] = CommandObjectSP (new CommandObjectApropos ()); + //m_command_dict["args"] = CommandObjectSP (new CommandObjectArgs ()); + m_command_dict["breakpoint"]= CommandObjectSP (new CommandObjectMultiwordBreakpoint (this)); + m_command_dict["call"] = CommandObjectSP (new CommandObjectCall ()); + m_command_dict["disassemble"] = CommandObjectSP (new CommandObjectDisassemble ()); + m_command_dict["expression"]= CommandObjectSP (new CommandObjectExpression ()); + m_command_dict["file"] = CommandObjectSP (new CommandObjectFile ()); + m_command_dict["frame"] = CommandObjectSP (new CommandObjectMultiwordFrame (this)); + m_command_dict["help"] = CommandObjectSP (new CommandObjectHelp ()); + m_command_dict["image"] = CommandObjectSP (new CommandObjectImage (this)); + m_command_dict["log"] = CommandObjectSP (new CommandObjectLog (this)); + m_command_dict["memory"] = CommandObjectSP (new CommandObjectMemory (this)); + m_command_dict["process"] = CommandObjectSP (new CommandObjectMultiwordProcess (this)); + m_command_dict["quit"] = CommandObjectSP (new CommandObjectQuit ()); + m_command_dict["register"] = CommandObjectSP (new CommandObjectRegister (this)); + //m_command_dict["remove"] = CommandObjectSP (new CommandObjectRemove ()); + m_command_dict["script"] = CommandObjectSP (new CommandObjectScript (m_script_language)); + m_command_dict["set"] = CommandObjectSP (new CommandObjectSet ()); + m_command_dict["settings"] = CommandObjectSP (new CommandObjectSettings ()); + m_command_dict["show"] = CommandObjectSP (new CommandObjectShow ()); + m_command_dict["source"] = CommandObjectSP (new CommandObjectSource ()); + m_command_dict["source-file"] = CommandObjectSP (new CommandObjectSourceFile ()); + //m_command_dict["syntax"] = CommandObjectSP (new CommandObjectSyntax ()); + m_command_dict["status"] = CommandObjectSP (new CommandObjectStatus ()); + m_command_dict["target"] = CommandObjectSP (new CommandObjectMultiwordTarget (this)); + m_command_dict["thread"] = CommandObjectSP (new CommandObjectMultiwordThread (this)); + //m_command_dict["translate"] = CommandObjectSP (new CommandObjectTranslate ()); + m_command_dict["unalias"] = CommandObjectSP (new CommandObjectUnalias ()); + m_command_dict["variable"] = CommandObjectSP (new CommandObjectVariable (this)); + + std::auto_ptr<CommandObjectRegexCommand> + break_regex_cmd_ap(new CommandObjectRegexCommand ("regexp-break", + "Smart breakpoint command (using regular expressions).", + "regexp-break [<file>:<line>]\nregexp-break [<address>]\nregexp-break <...>", 2)); + if (break_regex_cmd_ap.get()) + { + if (break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2") && + break_regex_cmd_ap->AddRegexCommand("^(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1") && + break_regex_cmd_ap->AddRegexCommand("^[\"']?([-+]\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'") && + break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list") && + break_regex_cmd_ap->AddRegexCommand("^(-.*)$", "breakpoint set %1") && + break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1'")) + { + CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); + m_command_dict[break_regex_cmd_sp->GetCommandName ()] = break_regex_cmd_sp; + } + } +} + +int +CommandInterpreter::GetCommandNamesMatchingPartialString (const char *cmd_str, bool include_aliases, + StringList &matches) +{ + CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_str, matches); + + if (include_aliases) + { + CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_str, matches); + } + + return matches.GetSize(); +} + +CommandObjectSP +CommandInterpreter::GetCommandSP (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches) +{ + CommandObject::CommandMap::iterator pos; + CommandObjectSP ret_val; + + std::string cmd(cmd_cstr); + + if (HasCommands()) + { + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + ret_val = pos->second; + } + + if (include_aliases && HasAliases()) + { + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + ret_val = pos->second; + } + + if (HasUserCommands()) + { + pos = m_user_dict.find(cmd); + if (pos != m_user_dict.end()) + ret_val = pos->second; + } + + if (!exact && ret_val == NULL) + { + StringList local_matches; + if (matches == NULL) + matches = &local_matches; + + int num_cmd_matches = 0; + int num_alias_matches = 0; + int num_user_matches = 0; + if (HasCommands()) + { + num_cmd_matches = CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_cstr, *matches); + } + + if (num_cmd_matches == 1) + { + cmd.assign(matches->GetStringAtIndex(0)); + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + ret_val = pos->second; + } + + if (num_cmd_matches != 1 && include_aliases && HasAliases()) + { + num_alias_matches = CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_cstr, *matches); + + } + + if (num_alias_matches == 1) + { + cmd.assign(matches->GetStringAtIndex (num_cmd_matches)); + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + { + matches->Clear(); + matches->AppendString (cmd.c_str()); + + ret_val = pos->second; + } + } + + if (num_cmd_matches != 1 && num_alias_matches != 1 && HasUserCommands()) + { + num_user_matches = CommandObject::AddNamesMatchingPartialString (m_user_dict, cmd_cstr, *matches); + } + + if (num_user_matches == 1) + { + cmd.assign (matches->GetStringAtIndex (num_cmd_matches + num_alias_matches)); + + pos = m_user_dict.find (cmd); + if (pos != m_user_dict.end()) + { + matches->Clear(); + matches->AppendString (cmd.c_str()); + + ret_val = pos->second; + } + } + } + else { + if (matches) + matches->AppendString (cmd_cstr); + } + + + return ret_val; +} + +CommandObject * +CommandInterpreter::GetCommandObject (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches) +{ + return GetCommandSP (cmd_cstr, include_aliases, exact, matches).get(); +} + +bool +CommandInterpreter::CommandExists (const char *cmd) +{ + return m_command_dict.find(cmd) != m_command_dict.end(); +} + +bool +CommandInterpreter::AliasExists (const char *cmd) +{ + return m_alias_dict.find(cmd) != m_alias_dict.end(); +} + +bool +CommandInterpreter::UserCommandExists (const char *cmd) +{ + return m_user_dict.find(cmd) != m_user_dict.end(); +} + +void +CommandInterpreter::AddAlias (const char *alias_name, CommandObjectSP& command_obj_sp) +{ + m_alias_dict[alias_name] = command_obj_sp; +} + +bool +CommandInterpreter::RemoveAlias (const char *alias_name) +{ + CommandObject::CommandMap::iterator pos = m_alias_dict.find(alias_name); + if (pos != m_alias_dict.end()) + { + m_alias_dict.erase(pos); + return true; + } + return false; +} +bool +CommandInterpreter::RemoveUser (const char *alias_name) +{ + CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name); + if (pos != m_user_dict.end()) + { + m_user_dict.erase(pos); + return true; + } + return false; +} + +StateVariable * +CommandInterpreter::GetStateVariable(const char *name) +{ + VariableMap::const_iterator pos = m_variables.find(name); + if (pos != m_variables.end()) + return pos->second.get(); + return NULL; +} + +void +CommandInterpreter::GetAliasHelp (const char *alias_name, const char *command_name, StreamString &help_string) +{ + help_string.Printf ("'%s", command_name); + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp != NULL) + { + OptionArgVector *options = option_arg_vector_sp.get(); + for (int i = 0; i < options->size(); ++i) + { + OptionArgPair cur_option = (*options)[i]; + std::string opt = cur_option.first; + std::string value = cur_option.second; + if (opt.compare("<argument>") == 0) + { + help_string.Printf (" %s", value.c_str()); + } + else + { + help_string.Printf (" %s", opt.c_str()); + if ((value.compare ("<no-argument>") != 0) + && (value.compare ("<need-argument") != 0)) + { + help_string.Printf (" %s", value.c_str()); + } + } + } + } + + help_string.Printf ("'"); +} + +std::string +CommandInterpreter::FindLongestCommandWord (CommandObject::CommandMap &dict) +{ + CommandObject::CommandMap::const_iterator pos; + int max_len = 0; + CommandObjectSP cmd_sp; + std::string longest_word; + + for (pos = dict.begin(); pos != dict.end(); ++pos) + { + if ((max_len == 0) + || (strlen (pos->first.c_str()) > max_len)) + { + longest_word = pos->first; + max_len = strlen (longest_word.c_str()); + } + } + + return longest_word; +} + +void +CommandInterpreter::GetHelp (CommandReturnObject &result) +{ + CommandObject::CommandMap::const_iterator pos; + result.AppendMessage("The following is a list of built-in, permanent debugger commands:"); + result.AppendMessage(""); + std::string longest_word = FindLongestCommandWord (m_command_dict); + uint32_t max_len = strlen (longest_word.c_str()); + + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(), + max_len); + } + result.AppendMessage(""); + + if (m_alias_dict.size() > 0) + { + result.AppendMessage("The following is a list of your current command abbreviations (see 'alias' for more info):"); + result.AppendMessage(""); + longest_word = FindLongestCommandWord (m_alias_dict); + max_len = strlen (longest_word.c_str()); + for (pos = m_alias_dict.begin(); pos != m_alias_dict.end(); ++pos) + { + StreamString sstr; + StreamString translation_and_help; + std::string entry_name = pos->first; + std::string second_entry = pos->second.get()->GetCommandName(); + GetAliasHelp (pos->first.c_str(), pos->second->GetCommandName(), sstr); + + translation_and_help.Printf ("(%s) %s", sstr.GetData(), pos->second->GetHelp()); + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", + translation_and_help.GetData(), max_len); + } + result.AppendMessage(""); + } + + if (m_user_dict.size() > 0) + { + result.AppendMessage ("The following is a list of your current user-defined commands:"); + result.AppendMessage(""); + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + result.AppendMessageWithFormat ("%s -- %s\n", pos->first.c_str(), pos->second->GetHelp()); + } + result.AppendMessage(""); + } + + result.AppendMessage("For more information on any particular command, try 'help <command-name>'."); +} + +void +CommandInterpreter::ShowVariableValues (CommandReturnObject &result) +{ + result.AppendMessage ("Below is a list of all the debugger setting variables and their values:"); + + for (VariableMap::const_iterator pos = m_variables.begin(); pos != m_variables.end(); ++pos) + { + StateVariable *var = pos->second.get(); + var->AppendVariableInformation (result); + } +} + +void +CommandInterpreter::ShowVariableHelp (CommandReturnObject &result) +{ + result.AppendMessage ("Below is a list of all the internal debugger variables that are settable:"); + for (VariableMap::const_iterator pos = m_variables.begin(); pos != m_variables.end(); ++pos) + { + StateVariable *var = pos->second.get(); + result.AppendMessageWithFormat (" %s -- %s \n", var->GetName(), var->GetHelp()); + } +} + +// Main entry point into the command_interpreter; this function takes a text +// line containing a debugger command, with all its flags, options, etc, +// parses the line and takes the appropriate actions. + +bool +CommandInterpreter::HandleCommand (const char *command_line, bool add_to_history, CommandReturnObject &result, + ExecutionContext *override_context) +{ + // FIXME: there should probably be a mutex to make sure only one thread can + // run the interpreter at a time. + + // TODO: this should be a logging channel in lldb. +// if (DebugSelf()) +// { +// result.AppendMessageWithFormat ("Processing command: %s\n", command_line); +// } + + m_current_context.Update (override_context); + + if (command_line == NULL || command_line[0] == '\0') + { + if (m_command_history.empty()) + { + result.AppendError ("empty command"); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + { + command_line = m_command_history.back().c_str(); + } + add_to_history = false; + } + + Args command_args(command_line); + + if (command_args.GetArgumentCount() > 0) + { + const char *command_cstr = command_args.GetArgumentAtIndex(0); + if (command_cstr) + { + + // We're looking up the command object here. So first find an exact match to the + // command in the commands. + + CommandObject *command_obj = GetCommandObject (command_cstr, false, true); + + // If we didn't find an exact match to the command string in the commands, look in + // the aliases. + + if (command_obj == NULL) + { + command_obj = GetCommandObject (command_cstr, true, true); + if (command_obj != NULL) + { + BuildAliasCommandArgs (command_obj, command_cstr, command_args, result); + if (!result.Succeeded()) + return false; + } + } + + // Finally, if there wasn't an exact match among the aliases, look for an inexact match. + + if (command_obj == NULL) + command_obj = GetCommandObject(command_cstr, false, false); + + if (command_obj) + { + if (command_obj->WantsRawCommandString()) + { + const char *stripped_command = ::strstr (command_line, command_cstr); + if (stripped_command) + { + stripped_command += strlen(command_cstr); + while (isspace(*stripped_command)) + ++stripped_command; + command_obj->ExecuteRawCommandString(stripped_command, Context(), this, result); + } + } + else + { + if (add_to_history) + m_command_history.push_back (command_line); + + // Remove the command from the args. + command_args.Shift(); + command_obj->ExecuteWithOptions (command_args, Context(), this, result); + } + } + else + { + StringList matches; + int num_matches; + int cursor_index = command_args.GetArgumentCount() - 1; + int cursor_char_position = strlen (command_args.GetArgumentAtIndex(command_args.GetArgumentCount() - 1)); + num_matches = HandleCompletionMatches (command_args, cursor_index, + cursor_char_position, + 0, -1, matches); + + if (num_matches > 0) + { + std::string error_msg; + error_msg.assign ("ambiguous command '"); + error_msg.append(command_cstr); + error_msg.append ("'."); + + error_msg.append (" Possible completions:"); + for (int i = 0; i < num_matches; i++) + { + error_msg.append ("\n\t"); + error_msg.append (matches.GetStringAtIndex (i)); + } + error_msg.append ("\n"); + result.AppendRawError (error_msg.c_str(), error_msg.size()); + } + else + result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_cstr); + + result.SetStatus (eReturnStatusFailed); + } + } + } + return result.Succeeded(); +} + +int +CommandInterpreter::HandleCompletionMatches (Args &parsed_line, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + StringList &matches) +{ + int num_command_matches = 0; + bool include_aliases = true; + bool look_for_subcommand = false; + + if (cursor_index == -1) + { + // We got nothing on the command line, so return the list of commands + num_command_matches = GetCommandNamesMatchingPartialString ("", include_aliases, matches); + } + else if (cursor_index == 0) + { + // The cursor is in the first argument, so just do a lookup in the dictionary. + CommandObject *cmd_obj = GetCommandObject (parsed_line.GetArgumentAtIndex(0), include_aliases, false, + &matches); + num_command_matches = matches.GetSize(); + + if (num_command_matches == 1 + && cmd_obj && cmd_obj->IsMultiwordObject() + && matches.GetStringAtIndex(0) != NULL + && strcmp (parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + look_for_subcommand = true; + num_command_matches = 0; + matches.DeleteStringAtIndex(0); + parsed_line.AppendArgument (""); + cursor_index++; + cursor_char_position = 0; + } + } + + if (cursor_index > 0 || look_for_subcommand) + { + // We are completing further on into a commands arguments, so find the command and tell it + // to complete the command. + // First see if there is a matching initial command: + CommandObject *command_object = GetCommandObject (parsed_line.GetArgumentAtIndex(0), include_aliases, false); + if (command_object == NULL) + { + return 0; + } + else + { + parsed_line.Shift(); + cursor_index--; + num_command_matches = command_object->HandleCompletion (parsed_line, cursor_index, cursor_char_position, + match_start_point, max_return_elements, this, + matches); + } + } + + return num_command_matches; + +} + +int +CommandInterpreter::HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + StringList &matches) +{ + // We parse the argument up to the cursor, so the last argument in parsed_line is + // the one containing the cursor, and the cursor is after the last character. + + Args parsed_line(current_line, last_char - current_line); + Args partial_parsed_line(current_line, cursor - current_line); + + int num_args = partial_parsed_line.GetArgumentCount(); + int cursor_index = partial_parsed_line.GetArgumentCount() - 1; + int cursor_char_position; + + if (cursor_index == -1) + cursor_char_position = 0; + else + cursor_char_position = strlen (partial_parsed_line.GetArgumentAtIndex(cursor_index)); + + int num_command_matches; + + matches.Clear(); + + // Only max_return_elements == -1 is supported at present: + assert (max_return_elements == -1); + num_command_matches = HandleCompletionMatches (parsed_line, cursor_index, cursor_char_position, match_start_point, + max_return_elements, matches); + + if (num_command_matches <= 0) + return num_command_matches; + + if (num_args == 0) + { + // If we got an empty string, insert nothing. + matches.InsertStringAtIndex(0, ""); + } + else + { + // Now figure out if there is a common substring, and if so put that in element 0, otherwise + // put an empty string in element 0. + std::string command_partial_str; + if (cursor_index >= 0) + command_partial_str.assign(parsed_line.GetArgumentAtIndex(cursor_index), parsed_line.GetArgumentAtIndex(cursor_index) + cursor_char_position); + + std::string common_prefix; + matches.LongestCommonPrefix (common_prefix); + int partial_name_len = command_partial_str.size(); + + // If we matched a unique single command, add a space... + if (num_command_matches == 1) + { + char quote_char = parsed_line.GetArgumentQuoteCharAtIndex(cursor_index); + if (quote_char != '\0') + common_prefix.push_back(quote_char); + + common_prefix.push_back(' '); + } + common_prefix.erase (0, partial_name_len); + matches.InsertStringAtIndex(0, common_prefix.c_str()); + } + return num_command_matches; +} + +CommandContext * +CommandInterpreter::Context () +{ + return &m_current_context; +} + +const Args * +CommandInterpreter::GetProgramArguments () +{ + if (! HasInterpreterVariables()) + return NULL; + + VariableMap::const_iterator pos = m_variables.find("run-args"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + + if (var) + return &var->GetArgs(); + return NULL; +} + +const Args * +CommandInterpreter::GetEnvironmentVariables () +{ + if (! HasInterpreterVariables()) + return NULL; + + VariableMap::const_iterator pos = m_variables.find("env-vars"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + if (var) + return &var->GetArgs(); + return NULL; +} + + +CommandInterpreter::~CommandInterpreter () +{ +} + +const char * +CommandInterpreter::GetPrompt () +{ + VariableMap::iterator pos; + + if (! HasInterpreterVariables()) + return NULL; + + pos = m_variables.find("prompt"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + + return ((char *) var->GetStringValue()); +} + +void +CommandInterpreter::SetPrompt (const char *new_prompt) +{ + VariableMap::iterator pos; + CommandReturnObject result; + + if (! HasInterpreterVariables()) + return; + + pos = m_variables.find ("prompt"); + if (pos == m_variables.end()) + return; + + StateVariable *var = pos->second.get(); + + if (var->VerifyValue (this, (void *) new_prompt, result)) + var->SetStringValue (new_prompt); +} + +void +CommandInterpreter::CrossRegisterCommand (const char * dest_cmd, const char * object_type) +{ + CommandObjectSP cmd_obj_sp = GetCommandSP (dest_cmd); + + if (cmd_obj_sp != NULL) + { + CommandObject *cmd_obj = cmd_obj_sp.get(); + if (cmd_obj->IsCrossRefObject ()) + cmd_obj->AddObject (object_type); + } +} + +void +CommandInterpreter::SetScriptLanguage (ScriptLanguage lang) +{ + m_script_language = lang; +} + +Listener * +CommandInterpreter::GetListener () +{ + return m_listener; +} + +SourceManager & +CommandInterpreter::GetSourceManager () +{ + return m_source_manager; +} + + + +OptionArgVectorSP +CommandInterpreter::GetAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos; + OptionArgVectorSP ret_val; + + std::string alias (alias_name); + + if (HasAliasOptions()) + { + pos = m_alias_options.find (alias); + if (pos != m_alias_options.end()) + ret_val = pos->second; + } + + return ret_val; +} + +void +CommandInterpreter::RemoveAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos = m_alias_options.find(alias_name); + if (pos != m_alias_options.end()) + { + m_alias_options.erase (pos); + } +} + +void +CommandInterpreter::AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp) +{ + m_alias_options[alias_name] = option_arg_vector_sp; +} + +bool +CommandInterpreter::HasCommands () +{ + return (!m_command_dict.empty()); +} + +bool +CommandInterpreter::HasAliases () +{ + return (!m_alias_dict.empty()); +} + +bool +CommandInterpreter::HasUserCommands () +{ + return (!m_user_dict.empty()); +} + +bool +CommandInterpreter::HasAliasOptions () +{ + return (!m_alias_options.empty()); +} + +bool +CommandInterpreter::HasInterpreterVariables () +{ + return (!m_variables.empty()); +} + +void +CommandInterpreter::BuildAliasCommandArgs +( + CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + CommandReturnObject &result +) +{ + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp.get()) + { + // Make sure that the alias name is the 0th element in cmd_args + std::string alias_name_str = alias_name; + if (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0) + cmd_args.Unshift (alias_name); + + Args new_args (alias_cmd_obj->GetCommandName()); + if (new_args.GetArgumentCount() == 2) + new_args.Shift(); + + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + int old_size = cmd_args.GetArgumentCount(); + int *used = (int *) malloc ((old_size + 1) * sizeof (int)); + + memset (used, 0, (old_size + 1) * sizeof (int)); + used[0] = 1; + + for (int i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + std::string option = option_pair.first; + std::string value = option_pair.second; + if (option.compare ("<argument>") == 0) + new_args.AppendArgument (value.c_str()); + else + { + new_args.AppendArgument (option.c_str()); + if (value.compare ("<no-argument>") != 0) + { + int index = GetOptionArgumentPosition (value.c_str()); + if (index == 0) + // value was NOT a positional argument; must be a real value + new_args.AppendArgument (value.c_str()); + else if (index >= cmd_args.GetArgumentCount()) + { + result.AppendErrorWithFormat + ("Not enough arguments provided; you need at least %d arguments to use this alias.\n", + index); + result.SetStatus (eReturnStatusFailed); + return; + } + else + { + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (index)); + used[index] = 1; + } + } + } + } + + for (int j = 0; j < cmd_args.GetArgumentCount(); ++j) + { + if (!used[j]) + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (j)); + } + + cmd_args.Clear(); + cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector()); + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + // This alias was not created with any options; nothing further needs to be done. + return; + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return; +} + + +int +CommandInterpreter::GetOptionArgumentPosition (const char *in_string) +{ + int position = 0; // Any string that isn't an argument position, i.e. '%' followed by an integer, gets a position + // of zero. + + char *cptr = (char *) in_string; + + // Does it start with '%' + if (cptr[0] == '%') + { + ++cptr; + + // Is the rest of it entirely digits? + if (isdigit (cptr[0])) + { + const char *start = cptr; + while (isdigit (cptr[0])) + ++cptr; + + // We've gotten to the end of the digits; are we at the end of the string? + if (cptr[0] == '\0') + position = atoi (start); + } + } + + return position; +} + +void +CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result) +{ + const char *init_file_path = in_cwd ? "./.lldbinit" : "~/.lldbinit"; + FileSpec init_file (init_file_path); + // If the file exists, tell HandleCommand to 'source' it; this will do the actual broadcasting + // of the commands back to any appropriate listener (see CommandObjectSource::Execute for more details). + + if (init_file.Exists()) + { + char path[PATH_MAX]; + init_file.GetPath(path, sizeof(path)); + StreamString source_command; + source_command.Printf ("source '%s'", path); + HandleCommand (source_command.GetData(), false, result); + } + else + { + // nothing to be done if the file doesn't exist + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } +} + +ScriptInterpreter * +CommandInterpreter::GetScriptInterpreter () +{ + CommandObject::CommandMap::iterator pos; + + pos = m_command_dict.find ("script"); + if (pos != m_command_dict.end()) + { + CommandObject *script_cmd_obj = pos->second.get(); + return ((CommandObjectScript *) script_cmd_obj)->GetInterpreter (); + } + else + return NULL; +} + + + +bool +CommandInterpreter::GetSynchronous () +{ + return m_synchronous_execution; +} + +void +CommandInterpreter::SetSynchronous (bool value) +{ + static bool value_set_once = false; + if (!value_set_once) + { + value_set_once = true; + m_synchronous_execution = value; + } +} + +void +CommandInterpreter::OutputFormattedHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + uint32_t max_word_len) +{ + StateVariable *var = GetStateVariable ("term-width"); + int max_columns = var->GetIntValue(); + // Sanity check max_columns, to cope with emacs shell mode with TERM=dumb + // (0 rows; 0 columns;). + if (max_columns <= 0) max_columns = 80; + + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + int len = indent_size + strlen (help_text) + 1; + char *text = (char *) malloc (len); + sprintf (text, "%-*s %s %s", max_word_len, word_text, separator, help_text); + if (text[len - 1] == '\n') + text[--len] = '\0'; + + if (len < max_columns) + { + // Output it as a single line. + strm.Printf ("%s", text); + } + else + { + // We need to break it up into multiple lines. + bool first_line = true; + int text_width; + int start = 0; + int end = start; + int final_end = strlen (text); + int sub_len; + + while (end < final_end) + { + if (first_line) + text_width = max_columns - 1; + else + text_width = max_columns - indent_size - 1; + + // Don't start the 'text' on a space, since we're already outputting the indentation. + if (!first_line) + { + while ((start < final_end) && (text[start] == ' ')) + start++; + } + + end = start + text_width; + if (end > final_end) + end = final_end; + else + { + // If we're not at the end of the text, make sure we break the line on white space. + while (end > start + && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') + end--; + } + + sub_len = end - start; + if (start != 0) + strm.EOL(); + if (!first_line) + strm.Indent(); + else + first_line = false; + assert (start <= final_end); + assert (start + sub_len <= final_end); + if (sub_len > 0) + strm.Write (text + start, sub_len); + start = end + 1; + } + } + strm.EOL(); + strm.IndentLess(indent_size); + free (text); +} + +void +CommandInterpreter::AproposAllSubCommands (CommandObject *cmd_obj, const char *prefix, const char *search_word, + StringList &commands_found, StringList &commands_help) +{ + CommandObject::CommandMap::const_iterator pos; + CommandObject::CommandMap sub_cmd_dict = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict; + CommandObject *sub_cmd_obj; + + for (pos = sub_cmd_dict.begin(); pos != sub_cmd_dict.end(); ++pos) + { + const char * command_name = pos->first.c_str(); + sub_cmd_obj = pos->second.get(); + StreamString complete_command_name; + + complete_command_name.Printf ("%s %s", prefix, command_name); + + if (sub_cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (complete_command_name.GetData()); + commands_help.AppendString (sub_cmd_obj->GetHelp()); + } + + if (sub_cmd_obj->IsMultiwordObject()) + AproposAllSubCommands (sub_cmd_obj, complete_command_name.GetData(), search_word, commands_found, + commands_help); + } + +} + +void +CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found, + StringList &commands_help) +{ + CommandObject::CommandMap::const_iterator pos; + + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + AproposAllSubCommands (cmd_obj, command_name, search_word, commands_found, commands_help); + + } +} diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp new file mode 100644 index 00000000000..080b5b057bf --- /dev/null +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -0,0 +1,448 @@ +//===-- CommandObject.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandObject.h" + +#include <string> +#include <map> + +#include <getopt.h> +#include <stdlib.h> +#include <ctype.h> + +#include "lldb/Core/Address.h" +#include "lldb/Core/Options.h" + +// These are for the Sourcename completers. +// FIXME: Make a separate file for the completers. +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObject +//------------------------------------------------------------------------- + +CommandObject::CommandObject (const char *name, const char *help, const char *syntax, uint32_t flags) : + m_cmd_name (name), + m_cmd_help_short (), + m_cmd_help_long (), + m_cmd_syntax (), + m_flags (flags) +{ + if (help && help[0]) + m_cmd_help_short = help; + if (syntax && syntax[0]) + m_cmd_syntax = syntax; +} + +CommandObject::~CommandObject () +{ +} + +const char * +CommandObject::GetHelp () +{ + return m_cmd_help_short.c_str(); +} + +const char * +CommandObject::GetHelpLong () +{ + return m_cmd_help_long.c_str(); +} + +const char * +CommandObject::GetSyntax () +{ + return m_cmd_syntax.c_str(); +} + +const char * +CommandObject::Translate () +{ + //return m_cmd_func_name.c_str(); + return "This function is currently not implemented."; +} + +const char * +CommandObject::GetCommandName () +{ + return m_cmd_name.c_str(); +} + +void +CommandObject::SetCommandName (const char *name) +{ + m_cmd_name = name; +} + +void +CommandObject::SetHelp (const char *cstr) +{ + m_cmd_help_short = cstr; +} + +void +CommandObject::SetHelpLong (const char *cstr) +{ + m_cmd_help_long = cstr; +} + +void +CommandObject::SetSyntax (const char *cstr) +{ + m_cmd_syntax = cstr; +} + +Options * +CommandObject::GetOptions () +{ + // By default commands don't have options unless this virtual function + // is overridden by base classes. + return NULL; +} + +Flags& +CommandObject::GetFlags() +{ + return m_flags; +} + +const Flags& +CommandObject::GetFlags() const +{ + return m_flags; +} + +bool +CommandObject::ExecuteCommandString +( + const char *command_line, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Args command_args(command_line); + return ExecuteWithOptions (command_args, context, interpreter, result); +} + +bool +CommandObject::ParseOptions +( + Args& args, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + // See if the subclass has options? + Options *options = GetOptions(); + if (options != NULL) + { + Error error; + options->ResetOptionValues(); + + // ParseOptions calls getopt_long, which always skips the zero'th item in the array and starts at position 1, + // so we need to push a dummy value into position zero. + args.Unshift("dummy_string"); + error = args.ParseOptions (*options); + + // The "dummy_string" will have already been removed by ParseOptions, + // so no need to remove it. + + if (error.Fail() || !options->VerifyOptions (result)) + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + { + // We got an error string, lets use that + result.GetErrorStream().PutCString(error_cstr); + } + else + { + // No error string, output the usage information into result + options->GenerateOptionUsage (result.GetErrorStream(), this); + } + // Set the return status to failed (this was an error). + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return true; +} +bool +CommandObject::ExecuteWithOptions +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + for (size_t i = 0; i < args.GetArgumentCount(); ++i) + { + const char *tmp_str = args.GetArgumentAtIndex (i); + if (tmp_str[0] == '`') // back-quote + args.ReplaceArgumentAtIndex (i, interpreter->ProcessEmbeddedScriptCommands (tmp_str)); + } + + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBeLaunched | CommandObject::eFlagProcessMustBePaused)) + { + result.AppendError ("Process must exist."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + StateType state = process->GetState(); + + switch (state) + { + + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + break; + + case eStateDetached: + case eStateExited: + case eStateUnloaded: + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBeLaunched)) + { + result.AppendError ("Process must be launched."); + result.SetStatus (eReturnStatusFailed); + return false; + } + break; + + case eStateRunning: + case eStateStepping: + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBePaused)) + { + result.AppendError ("Process is running. Use 'process interrupt' to pause execution."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (!ParseOptions (args, interpreter, result)) + return false; + + // Call the command-specific version of 'Execute', passing it the already processed arguments. + return Execute (args, context, interpreter, result); +} + +class CommandDictCommandPartialMatch +{ + public: + CommandDictCommandPartialMatch (const char *match_str) + { + m_match_str = match_str; + } + bool operator() (const std::pair<std::string, lldb::CommandObjectSP> map_element) const + { + // A NULL or empty string matches everything. + if (m_match_str == NULL || *m_match_str == '\0') + return 1; + + size_t found = map_element.first.find (m_match_str, 0); + if (found == std::string::npos) + return 0; + else + return found == 0; + } + + private: + const char *m_match_str; +}; + +int +CommandObject::AddNamesMatchingPartialString (CommandObject::CommandMap &in_map, const char *cmd_str, + StringList &matches) +{ + int number_added = 0; + CommandDictCommandPartialMatch matcher(cmd_str); + + CommandObject::CommandMap::iterator matching_cmds = std::find_if (in_map.begin(), in_map.end(), matcher); + + while (matching_cmds != in_map.end()) + { + ++number_added; + matches.AppendString((*matching_cmds).first.c_str()); + matching_cmds = std::find_if (++matching_cmds, in_map.end(), matcher);; + } + return number_added; +} + +int +CommandObject::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) +{ + if (WantsRawCommandString()) + { + // FIXME: Abstract telling the completion to insert the completion character. + matches.Clear(); + return -1; + } + else + { + // Can we do anything generic with the options? + Options *cur_options = GetOptions(); + CommandReturnObject result; + OptionElementVector opt_element_vector; + + if (cur_options != NULL) + { + // Re-insert the dummy command name string which will have been + // stripped off: + input.Unshift ("dummy-string"); + cursor_index++; + + + // I stick an element on the end of the input, because if the last element is + // option that requires an argument, getopt_long will freak out. + + input.AppendArgument ("<FAKE-VALUE>"); + + input.ParseArgsForCompletion (*cur_options, opt_element_vector); + + input.DeleteArgumentAtIndex(input.GetArgumentCount() - 1); + + bool handled_by_options; + handled_by_options = cur_options->HandleOptionCompletion(input, + opt_element_vector, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + interpreter, + matches); + if (handled_by_options) + return matches.GetSize(); + } + + // If we got here, the last word is not an option or an option argument. + return HandleArgumentCompletion(input, + cursor_index, + cursor_char_position, + opt_element_vector, + match_start_point, + max_return_elements, + interpreter, + matches); + } +} + +int +CommandObject::HandleArgumentCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) +{ + return 0; +} + +// Case insensitive version of ::strstr() +// Returns true if s2 is contained within s1. + +static bool +contains_string (const char *s1, const char *s2) +{ + char *locase_s1 = (char *) malloc (strlen (s1) + 1); + char *locase_s2 = (char *) malloc (strlen (s2) + 1); + int i; + for (i = 0; s1 && s1[i] != '\0'; i++) + locase_s1[i] = ::tolower (s1[i]); + locase_s1[i] = '\0'; + for (i = 0; s2 && s2[i] != '\0'; i++) + locase_s2[i] = ::tolower (s2[i]); + locase_s2[i] = '\0'; + + const char *result = ::strstr (locase_s1, locase_s2); + free (locase_s1); + free (locase_s2); + // 'result' points into freed memory - but we're not + // deref'ing it so hopefully current/future compilers + // won't complain.. + + if (result == NULL) + return false; + else + return true; +} + +bool +CommandObject::HelpTextContainsWord (const char *search_word) +{ + const char *short_help; + const char *long_help; + const char *syntax_help; + std::string options_usage_help; + + + bool found_word = false; + + short_help = GetHelp(); + long_help = GetHelpLong(); + syntax_help = GetSyntax(); + + if (contains_string (short_help, search_word)) + found_word = true; + else if (contains_string (long_help, search_word)) + found_word = true; + else if (contains_string (syntax_help, search_word)) + found_word = true; + + if (!found_word + && GetOptions() != NULL) + { + StreamString usage_help; + GetOptions()->GenerateOptionUsage (usage_help, this); + if (usage_help.GetSize() > 0) + { + const char *usage_text = usage_help.GetData(); + if (contains_string (usage_text, search_word)) + found_word = true; + } + } + + return found_word; +} diff --git a/lldb/source/Interpreter/CommandObjectCrossref.cpp b/lldb/source/Interpreter/CommandObjectCrossref.cpp new file mode 100644 index 00000000000..27b66379e87 --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectCrossref.cpp @@ -0,0 +1,92 @@ +//===-- CommandObjectCrossref.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandObjectCrossref.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectCrossref +//------------------------------------------------------------------------- + +CommandObjectCrossref::CommandObjectCrossref +( + const char *name, + const char *help, + const char *syntax +) : + CommandObject (name, help, syntax), + m_crossref_object_types() +{ +} + +CommandObjectCrossref::~CommandObjectCrossref () +{ +} + +bool +CommandObjectCrossref::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + if (m_crossref_object_types.GetArgumentCount() == 0) + { + result.AppendErrorWithFormat ("There are no objects for which you can call '%s'.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + else + { + GenerateHelpText (result); + } + return result.Succeeded(); +} + +void +CommandObjectCrossref::AddObject (const char *obj_name) +{ + m_crossref_object_types.AppendArgument (obj_name); +} + +const char ** +CommandObjectCrossref::GetObjectTypes () const +{ + return m_crossref_object_types.GetConstArgumentVector(); +} + +void +CommandObjectCrossref::GenerateHelpText (CommandReturnObject &result) +{ + result.AppendMessage ("This command can be called on the following types of objects:"); + + for (int i = 0; i < m_crossref_object_types.GetArgumentCount(); ++i) + { + const char *obj_name = m_crossref_object_types.GetArgumentAtIndex(i); + result.AppendMessageWithFormat (" %s (e.g. '%s %s')\n", obj_name, + obj_name, GetCommandName()); + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +bool +CommandObjectCrossref::IsCrossRefObject () +{ + return true; +} diff --git a/lldb/source/Interpreter/CommandObjectMultiword.cpp b/lldb/source/Interpreter/CommandObjectMultiword.cpp new file mode 100644 index 00000000000..874be0ea6a1 --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectMultiword.cpp @@ -0,0 +1,263 @@ +//===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandObjectMultiword.h" +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectMultiword +//------------------------------------------------------------------------- + +CommandObjectMultiword::CommandObjectMultiword +( + const char *name, + const char *help, + const char *syntax, + uint32_t flags +) : + CommandObject (name, help, syntax, flags) +{ +} + +CommandObjectMultiword::~CommandObjectMultiword () +{ +} + +CommandObjectSP +CommandObjectMultiword::GetSubcommandSP (const char *sub_cmd, StringList *matches) +{ + CommandObjectSP return_cmd_sp; + CommandObject::CommandMap::iterator pos; + + if (!m_subcommand_dict.empty()) + { + pos = m_subcommand_dict.find (sub_cmd); + if (pos != m_subcommand_dict.end()) + return_cmd_sp = pos->second; + else + { + + StringList local_matches; + if (matches == NULL) + matches = &local_matches; + int num_matches = CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, sub_cmd, *matches); + + if (num_matches == 1) + { + // Cleaner, but slightly less efficient would be to call back into this function, since I now + // know I have an exact match... + + sub_cmd = matches->GetStringAtIndex(0); + pos = m_subcommand_dict.find(sub_cmd); + if (pos != m_subcommand_dict.end()) + return_cmd_sp = pos->second; + } + } + } + return return_cmd_sp; +} + +CommandObject * +CommandObjectMultiword::GetSubcommandObject (const char *sub_cmd, StringList *matches) +{ + return GetSubcommandSP(sub_cmd, matches).get(); +} + +bool +CommandObjectMultiword::LoadSubCommand (CommandObjectSP cmd_obj, const char *name, + CommandInterpreter *interpreter) +{ + CommandMap::iterator pos; + bool success = true; + + pos = m_subcommand_dict.find(name); + if (pos == m_subcommand_dict.end()) + { + m_subcommand_dict[name] = cmd_obj; + interpreter->CrossRegisterCommand (name, GetCommandName()); + } + else + success = false; + + return success; +} + +bool +CommandObjectMultiword::Execute +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + GenerateHelpText (result, interpreter); + } + else + { + const char *sub_command = args.GetArgumentAtIndex (0); + + if (sub_command) + { + if (::strcasecmp (sub_command, "help") == 0) + { + GenerateHelpText (result, interpreter); + } + else if (!m_subcommand_dict.empty()) + { + StringList matches; + CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); + if (sub_cmd_obj != NULL) + { + // Now call CommandObject::Execute to process and options in 'rest_of_line'. From there + // the command-specific version of Execute will be called, with the processed arguments. + + args.Shift(); + + sub_cmd_obj->ExecuteWithOptions (args, context, interpreter, result); + } + else + { + std::string error_msg; + int num_subcmd_matches = matches.GetSize(); + if (num_subcmd_matches > 0) + error_msg.assign ("ambiguous command "); + else + error_msg.assign ("invalid command "); + + error_msg.append ("'"); + error_msg.append (GetCommandName()); + error_msg.append (" "); + error_msg.append (sub_command); + error_msg.append ("'"); + + if (num_subcmd_matches > 0) + { + error_msg.append (" Possible completions:"); + for (int i = 0; i < num_subcmd_matches; i++) + { + error_msg.append ("\n\t"); + error_msg.append (matches.GetStringAtIndex (i)); + } + } + error_msg.append ("\n"); + result.AppendRawError (error_msg.c_str(), error_msg.size()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' does not have any subcommands.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +void +CommandObjectMultiword::GenerateHelpText (CommandReturnObject &result, CommandInterpreter *interpreter) +{ + // First time through here, generate the help text for the object and + // push it to the return result object as well + + StreamString &output_stream = result.GetOutputStream(); + output_stream.PutCString ("The following subcommands are supported:\n\n"); + + CommandMap::iterator pos; + std::string longest_word = interpreter->FindLongestCommandWord (m_subcommand_dict); + uint32_t max_len = 0; + + if (! longest_word.empty()) + max_len = strlen (longest_word.c_str()) + 4; // Indent the output by 4 spaces. + + for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) + { + std::string indented_command (" "); + indented_command.append (pos->first); + interpreter->OutputFormattedHelpText (result.GetOutputStream(), indented_command.c_str(), "--", + pos->second->GetHelp(), max_len); + } + + output_stream.PutCString ("\nFor more help on any particular subcommand, type 'help <command> <subcommand>'.\n"); + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +int +CommandObjectMultiword::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) +{ + if (cursor_index == 0) + { + CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, input.GetArgumentAtIndex(0), matches); + + if (matches.GetSize() == 1 + && matches.GetStringAtIndex(0) != NULL + && strcmp (input.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + StringList temp_matches; + CommandObject *cmd_obj = GetSubcommandObject (input.GetArgumentAtIndex(0), &temp_matches); + if (cmd_obj != NULL) + { + matches.DeleteStringAtIndex (0); + input.Shift(); + cursor_char_position = 0; + input.AppendArgument (""); + return cmd_obj->HandleCompletion (input, cursor_index, cursor_char_position, match_start_point, + max_return_elements, interpreter, matches); + } + else + return matches.GetSize(); + } + else + return matches.GetSize(); + } + else + { + CommandObject *sub_command_object = GetSubcommandObject (input.GetArgumentAtIndex(0), &matches); + if (sub_command_object == NULL) + { + return matches.GetSize(); + } + else + { + // Remove the one match that we got from calling GetSubcommandObject. + matches.DeleteStringAtIndex(0); + input.Shift(); + cursor_index--; + return sub_command_object->HandleCompletion (input, cursor_index, cursor_char_position, match_start_point, + max_return_elements, interpreter, matches); + } + + } +} + diff --git a/lldb/source/Interpreter/CommandObjectRegexCommand.cpp b/lldb/source/Interpreter/CommandObjectRegexCommand.cpp new file mode 100644 index 00000000000..b3fa6a41d97 --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectRegexCommand.cpp @@ -0,0 +1,123 @@ +//===-- CommandObjectRegexCommand.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandObjectRegexCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommandObjectRegexCommand constructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::CommandObjectRegexCommand +( + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches +) : + CommandObject (name, help, syntax), + m_entries(), + m_max_matches (max_matches) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::~CommandObjectRegexCommand() +{ +} + + +bool +CommandObjectRegexCommand::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + return false; +} + + +bool +CommandObjectRegexCommand::ExecuteRawCommandString +( + const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + if (command) + { + EntryCollection::const_iterator pos, end = m_entries.end(); + for (pos = m_entries.begin(); pos != end; ++pos) + { + if (pos->regex.Execute (command, m_max_matches)) + { + std::string new_command(pos->command); + std::string match_str; + char percent_var[8]; + size_t idx, percent_var_idx; + for (uint32_t match_idx=1; match_idx <= m_max_matches; ++match_idx) + { + if (pos->regex.GetMatchAtIndex (command, match_idx, match_str)) + { + const int percent_var_len = ::snprintf (percent_var, sizeof(percent_var), "%%%u", match_idx); + for (idx = 0; (percent_var_idx = new_command.find(percent_var, idx)) != std::string::npos; ) + { + new_command.erase(percent_var_idx, percent_var_len); + new_command.insert(percent_var_idx, match_str); + idx += percent_var_idx + match_str.size(); + } + } + } + // Interpret the new command and return this as the result! +// if (m_options.verbose) +// result.GetOutputStream().Printf("%s\n", new_command.c_str()); + return interpreter->HandleCommand(new_command.c_str(), true, result); + } + } + result.SetStatus(eReturnStatusFailed); + result.AppendErrorWithFormat("Command contents '%s' failed to match any regular expression in the '%s' regex command.\n", + command, + m_cmd_name.c_str()); + return false; + } + result.AppendError("empty command passed to regular exression command"); + result.SetStatus(eReturnStatusFailed); + return false; +} + + +bool +CommandObjectRegexCommand::AddRegexCommand (const char *re_cstr, const char *command_cstr) +{ + m_entries.resize(m_entries.size() + 1); + // Only add the regular expression if it compiles + if (m_entries.back().regex.Compile (re_cstr, REG_EXTENDED)) + { + m_entries.back().command.assign (command_cstr); + return true; + } + // The regex didn't compile... + m_entries.pop_back(); + return false; +} diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp new file mode 100644 index 00000000000..f634e3c7df5 --- /dev/null +++ b/lldb/source/Interpreter/CommandReturnObject.cpp @@ -0,0 +1,175 @@ +//===-- CommandReturnObject.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandReturnObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +CommandReturnObject::CommandReturnObject () : + m_output_stream (), + m_error_stream (), + m_status (eReturnStatusStarted), + m_did_change_process_state (false) +{ +} + +CommandReturnObject::~CommandReturnObject () +{ +} + +StreamString & +CommandReturnObject::GetOutputStream () +{ + return m_output_stream; +} + +StreamString & +CommandReturnObject::GetErrorStream () +{ + return m_error_stream; +} + +void +CommandReturnObject::AppendErrorWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_error_stream.Printf("error: %s", sstrm.GetData()); +} + + +void +CommandReturnObject::AppendMessageWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_output_stream.Printf("%s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendWarningWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_error_stream.Printf("warning: %s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendMessage (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_output_stream.Printf("%*.*s\n", len, len, in_string); +} + +void +CommandReturnObject::AppendWarning (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf("warning: %*.*s\n", len, len, in_string); +} + +// Similar to AppendWarning, but do not prepend 'warning: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawWarning (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf("%*.*s", len, len, in_string); +} + +void +CommandReturnObject::AppendError (const char *in_string, int len) +{ + if (!in_string) + return; + + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf ("error: %*.*s\n", len, len, in_string); +} + +// Similar to AppendError, but do not prepend 'Error: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawError (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf ("%*.*s", len, len, in_string); +} + +void +CommandReturnObject::SetStatus (ReturnStatus status) +{ + m_status = status; +} + +ReturnStatus +CommandReturnObject::GetStatus () +{ + return m_status; +} + +bool +CommandReturnObject::Succeeded () +{ + return m_status <= eReturnStatusSuccessContinuingResult; +} + +bool +CommandReturnObject::HasResult () +{ + return (m_status == eReturnStatusSuccessFinishResult || + m_status == eReturnStatusSuccessContinuingResult); +} + +void +CommandReturnObject::Clear() +{ + m_output_stream.Clear(); + m_error_stream.Clear(); + m_status = eReturnStatusStarted; +} + +bool +CommandReturnObject::GetDidChangeProcessState () +{ + return m_did_change_process_state; +} + +void +CommandReturnObject::SetDidChangeProcessState (bool b) +{ + m_did_change_process_state = b; +} + diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp new file mode 100644 index 00000000000..fa3ac31e691 --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -0,0 +1,66 @@ +//===-- ScriptInterpreter.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/ScriptInterpreter.h" + +#include <string> +#include <stdlib.h> +#include <stdio.h> + +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreter::ScriptInterpreter (ScriptLanguage script_lang) : + m_script_lang (script_lang), + m_interpreter_pty () +{ + if (m_interpreter_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, NULL, 0)) + { + const char *slave_name = m_interpreter_pty.GetSlaveName(NULL, 0); + if (slave_name) + m_pty_slave_name.assign(slave_name); + } +} + +ScriptInterpreter::~ScriptInterpreter () +{ + m_interpreter_pty.CloseMasterFileDescriptor(); +} + +const char * +ScriptInterpreter::GetScriptInterpreterPtyName () +{ + return m_pty_slave_name.c_str(); +} + +int +ScriptInterpreter::GetMasterFileDescriptor () +{ + return m_interpreter_pty.GetMasterFileDescriptor(); +} + +void +ScriptInterpreter::CollectDataForBreakpointCommandCallback +( + BreakpointOptions *bp_options, + CommandReturnObject &result +) +{ + result.SetStatus (eReturnStatusFailed); + result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented."); +} + + diff --git a/lldb/source/Interpreter/ScriptInterpreterNone.cpp b/lldb/source/Interpreter/ScriptInterpreterNone.cpp new file mode 100644 index 00000000000..cdc399e7d86 --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreterNone.cpp @@ -0,0 +1,38 @@ +//===-- ScriptInterpreterNone.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreterNone::ScriptInterpreterNone () : + ScriptInterpreter (eScriptLanguageNone) +{ +} + +ScriptInterpreterNone::~ScriptInterpreterNone () +{ +} + +void +ScriptInterpreterNone::ExecuteOneLine (const std::string &line, FILE *out, FILE *err) +{ + ::fprintf (err, "error: there is no embedded script interpreter in this mode.\n"); +} + +void +ScriptInterpreterNone::ExecuteInterpreterLoop (FILE *out, FILE *err) +{ + fprintf (err, "error: there is no embedded script interpreter in this mode.\n"); +} + + diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp new file mode 100644 index 00000000000..6b2e3b2e7ad --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -0,0 +1,830 @@ +//===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In order to guarantee correct working with Python, Python.h *MUST* be +// the *FIRST* header file included: + +#include <Python.h> + +#include "lldb/Interpreter/ScriptInterpreterPython.h" + + +#include <sys/ioctl.h> +#include <termios.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string> + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" + +extern "C" void init_lldb (void); + +using namespace lldb; +using namespace lldb_private; + +const char embedded_interpreter_string[] = +"import readline\n\ +import code\n\ +import sys\n\ +import traceback\n\ +\n\ +class SimpleREPL(code.InteractiveConsole):\n\ + def __init__(self, prompt, dict):\n\ + code.InteractiveConsole.__init__(self,dict)\n\ + self.prompt = prompt\n\ + self.loop_exit = False\n\ + self.dict = dict\n\ +\n\ + def interact(self):\n\ + try:\n\ + sys.ps1\n\ + except AttributeError:\n\ + sys.ps1 = \">>> \"\n\ + try:\n\ + sys.ps2\n\ + except AttributeError:\n\ + sys.ps2 = \"... \"\n\ +\n\ + while not self.loop_exit:\n\ + try:\n\ + self.read_py_command()\n\ + except (SystemExit, EOFError):\n\ + # EOF while in Python just breaks out to top level.\n\ + self.write('\\n')\n\ + self.loop_exit = True\n\ + break\n\ + except KeyboardInterrupt:\n\ + self.write(\"\\nKeyboardInterrupt\\n\")\n\ + self.resetbuffer()\n\ + more = 0\n\ + except:\n\ + traceback.print_exc()\n\ +\n\ + def process_input (self, in_str):\n\ + # Canonicalize the format of the input string\n\ + temp_str = in_str\n\ + temp_str.strip(' \t')\n\ + words = temp_str.split()\n\ + temp_str = ('').join(words)\n\ +\n\ + # Check the input string to see if it was the quit\n\ + # command. If so, intercept it, so that it doesn't\n\ + # close stdin on us!\n\ + if (temp_str.lower() == \"quit()\"):\n\ + self.loop_exit = True\n\ + in_str = \"raise SystemExit \"\n\ + return in_str\n\ +\n\ + def my_raw_input (self, prompt):\n\ + stream = sys.stdout\n\ + stream.write (prompt)\n\ + stream.flush ()\n\ + try:\n\ + line = sys.stdin.readline()\n\ + except KeyboardInterrupt:\n\ + line = \" \\n\"\n\ + except (SystemExit, EOFError):\n\ + line = \"quit()\\n\"\n\ + if not line:\n\ + raise EOFError\n\ + if line[-1] == '\\n':\n\ + line = line[:-1]\n\ + return line\n\ +\n\ + def read_py_command(self):\n\ + # Read off a complete Python command.\n\ + more = 0\n\ + while 1:\n\ + if more:\n\ + prompt = sys.ps2\n\ + else:\n\ + prompt = sys.ps1\n\ + line = self.my_raw_input(prompt)\n\ + # Can be None if sys.stdin was redefined\n\ + encoding = getattr(sys.stdin, \"encoding\", None)\n\ + if encoding and not isinstance(line, unicode):\n\ + line = line.decode(encoding)\n\ + line = self.process_input (line)\n\ + more = self.push(line)\n\ + if not more:\n\ + break\n\ +\n\ +def run_python_interpreter (dict):\n\ + # Pass in the dictionary, for continuity from one session to the next.\n\ + repl = SimpleREPL('>>> ', dict)\n\ + repl.interact()\n"; + +static int +_check_and_flush (FILE *stream) +{ + int prev_fail = ferror (stream); + return fflush (stream) || prev_fail ? EOF : 0; +} + +ScriptInterpreterPython::ScriptInterpreterPython () : + ScriptInterpreter (eScriptLanguagePython), + m_compiled_module (NULL), + m_termios_valid (false) +{ + + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + // Find the module that owns this code and use that path we get to + // set the PYTHONPATH appropriately. + + FileSpec this_module (Host::GetModuleFileSpecForHostAddress ((void *)init_lldb)); + std::string python_path; + + if (this_module.GetDirectory()) + { + // Append the directory that the module that loaded this code + // belongs to + python_path += this_module.GetDirectory().AsCString(""); + +#if defined (__APPLE__) + // If we are running on MacOSX we might be in a framework and should + // add an appropriate path so Resource can be found in a bundle + + if (::strstr(this_module.GetDirectory().AsCString(""), ".framework")) + { + python_path.append(1, ':'); + python_path.append(this_module.GetDirectory().AsCString("")); + python_path.append("/Resources/Python"); + } +#endif + // The the PYTHONPATH environment variable so that Python can find + // our lldb.py module and our _lldb.so. + ::setenv ("PYTHONPATH", python_path.c_str(), 1); + } + + Py_Initialize (); + + PyObject *compiled_module = Py_CompileString (embedded_interpreter_string, "embedded_interpreter.py", + Py_file_input); + + m_compiled_module = compiled_module; + + init_lldb (); + + // Update the path python uses to search for modules to include the current directory. + + int success = PyRun_SimpleString ("import sys"); + success = PyRun_SimpleString ("sys.path.append ('.')"); + if (success == 0) + { + // Import the Script Bridge module. + success = PyRun_SimpleString ("from lldb import *"); + } + + const char *pty_slave_name = GetScriptInterpreterPtyName (); + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + + PyObject *pmod = PyImport_ExecCodeModule((char *)"embedded_interpreter", m_compiled_module); + if (pmod != NULL) + { + PyRun_SimpleString ("ConsoleDict = locals()"); + PyRun_SimpleString ("from embedded_interpreter import run_python_interpreter"); + PyRun_SimpleString ("import sys"); + PyRun_SimpleString ("from termios import *"); + PyRun_SimpleString ("old_stdin = sys.stdin"); + + StreamString run_string; + run_string.Printf ("new_stdin = open('%s', 'r')", pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + PyRun_SimpleString ("sys.stdin = new_stdin"); + + PyRun_SimpleString ("old_stdout = sys.stdout"); + + if (out_fh != NULL) + { + PyObject *new_sysout = PyFile_FromFile (out_fh, (char *) "", (char *) "w", + _check_and_flush); + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if ((new_sysout != NULL) + && (sysmod != NULL) + && (sysdict != NULL)) + { + PyDict_SetItemString (sysdict, "stdout", new_sysout); + } + + if (PyErr_Occurred()) + PyErr_Clear(); + } + + PyRun_SimpleString ("new_mode = tcgetattr(new_stdin)"); + PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON"); + PyRun_SimpleString ("new_mode[6][VEOF] = 255"); + PyRun_SimpleString ("tcsetattr (new_stdin, TCSANOW, new_mode)"); + } + + +} + +ScriptInterpreterPython::~ScriptInterpreterPython () +{ + PyRun_SimpleString ("sys.stdin = old_stdin"); + PyRun_SimpleString ("sys.stdout = old_stdout"); + Py_Finalize (); +} + +void +ScriptInterpreterPython::ExecuteOneLine (const std::string& line, FILE *out, FILE *err) +{ + int success; + + success = PyRun_SimpleString (line.c_str()); + if (success != 0) + { + fprintf (err, "error: python failed attempting to evaluate '%s'\n", line.c_str()); + } +} + + + +size_t +ScriptInterpreterPython::InputReaderCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + if (baton == NULL) + return 0; + + ScriptInterpreterPython *interpreter = (ScriptInterpreterPython *) baton; + switch (notification) + { + case eInputReaderActivate: + { + // Save terminal settings if we can + interpreter->m_termios_valid = ::tcgetattr (::fileno (reader->GetInputFileHandle()), + &interpreter->m_termios) == 0; + struct termios tmp_termios; + if (::tcgetattr (::fileno (reader->GetInputFileHandle()), &tmp_termios) == 0) + { + tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE; + ::tcsetattr (::fileno (reader->GetInputFileHandle()), TCSANOW, &tmp_termios); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len) + { + if ((int) bytes[0] == 4) + ::write (interpreter->GetMasterFileDescriptor(), "quit()", 6); + else + ::write (interpreter->GetMasterFileDescriptor(), bytes, bytes_len); + } + ::write (interpreter->GetMasterFileDescriptor(), "\n", 1); + break; + + case eInputReaderDone: + // Send a control D to the script interpreter + //::write (interpreter->GetMasterFileDescriptor(), "\nquit()\n", strlen("\nquit()\n")); + // Write a newline out to the reader output + //::fwrite ("\n", 1, 1, out_fh); + // Restore terminal settings if they were validly saved + if (interpreter->m_termios_valid) + { + ::tcsetattr (::fileno (reader->GetInputFileHandle()), + TCSANOW, + &interpreter->m_termios); + } + break; + } + + return bytes_len; +} + + +void +ScriptInterpreterPython::ExecuteInterpreterLoop (FILE *out, FILE *err) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + InputReaderSP reader_sp (new InputReader()); + if (reader_sp) + { + Error error (reader_sp->Initialize (ScriptInterpreterPython::InputReaderCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + + if (error.Success()) + { + Debugger::GetSharedInstance().PushInputReader (reader_sp); + ExecuteOneLine ("run_python_interpreter(ConsoleDict)", out, err); + Debugger::GetSharedInstance().PopInputReader (reader_sp); + } + } +} + +bool +ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ReturnType return_type, + void *ret_value) +{ + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = globals; + PyObject *py_error = NULL; + bool ret_success; + int success; + + if (in_string != NULL) + { + py_return = PyRun_String (in_string, Py_eval_input, globals, locals); + if (py_return == NULL) + { + py_error = PyErr_Occurred (); + if (py_error != NULL) + PyErr_Clear (); + + py_return = PyRun_String (in_string, Py_single_input, globals, locals); + } + + if (py_return != NULL) + { + switch (return_type) + { + case eCharPtr: // "char *" + { + const char format[3] = "s#"; + success = PyArg_Parse (py_return, format, (char **) &ret_value); + break; + } + case eBool: + { + const char format[2] = "b"; + success = PyArg_Parse (py_return, format, (bool *) ret_value); + break; + } + case eShortInt: + { + const char format[2] = "h"; + success = PyArg_Parse (py_return, format, (short *) ret_value); + break; + } + case eShortIntUnsigned: + { + const char format[2] = "H"; + success = PyArg_Parse (py_return, format, (unsigned short *) ret_value); + break; + } + case eInt: + { + const char format[2] = "i"; + success = PyArg_Parse (py_return, format, (int *) ret_value); + break; + } + case eIntUnsigned: + { + const char format[2] = "I"; + success = PyArg_Parse (py_return, format, (unsigned int *) ret_value); + break; + } + case eLongInt: + { + const char format[2] = "l"; + success = PyArg_Parse (py_return, format, (long *) ret_value); + break; + } + case eLongIntUnsigned: + { + const char format[2] = "k"; + success = PyArg_Parse (py_return, format, (unsigned long *) ret_value); + break; + } + case eLongLong: + { + const char format[2] = "L"; + success = PyArg_Parse (py_return, format, (long long *) ret_value); + break; + } + case eLongLongUnsigned: + { + const char format[2] = "K"; + success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value); + break; + } + case eFloat: + { + const char format[2] = "f"; + success = PyArg_Parse (py_return, format, (float *) ret_value); + break; + } + case eDouble: + { + const char format[2] = "d"; + success = PyArg_Parse (py_return, format, (double *) ret_value); + break; + } + case eChar: + { + const char format[2] = "c"; + success = PyArg_Parse (py_return, format, (char *) ret_value); + break; + } + default: + {} + } + Py_DECREF (py_return); + if (success) + ret_success = true; + else + ret_success = false; + } + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + ret_success = false; + } + + return ret_success; +} + +bool +ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) +{ + bool success = false; + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = globals; + PyObject *py_error = NULL; + + if (in_string != NULL) + { + struct _node *compiled_node = PyParser_SimpleParseString (in_string, Py_file_input); + if (compiled_node) + { + PyCodeObject *compiled_code = PyNode_Compile (compiled_node, "temp.py"); + if (compiled_code) + { + py_return = PyEval_EvalCode (compiled_code, globals, locals); + if (py_return != NULL) + { + success = true; + Py_DECREF (py_return); + } + } + } + } + + py_error = PyErr_Occurred (); + if (py_error != NULL) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + success = false; + } + + return success; +} + +static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end."; + +size_t +ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + FILE *out_fh = reader->GetOutputFileHandle(); + switch (notification) + { + case eInputReaderActivate: + { + commands_in_progress.Clear(); + if (out_fh) + { + ::fprintf (out_fh, "%s\n", g_reader_instructions); + if (reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader->GetPrompt() && out_fh) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + break; + + case eInputReaderGotToken: + { + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (out_fh && !reader->IsDone() && reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + } + break; + + case eInputReaderDone: + { + BreakpointOptions *bp_options = (BreakpointOptions *)baton; + std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = Debugger::GetSharedInstance().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateBreakpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + if (data_ap->script_source.GetSize() == 1) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + } + } + else + { + // FIXME: Error processing. + } + } + } + break; + + } + + return bytes_len; +} + +void +ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new InputReader ()); + + if (reader_sp) + { + Error err = reader_sp->Initialize ( + ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback, + bp_options, // baton + eInputReaderGranularityLine, // token size, for feeding data to callback function + "DONE", // end token + "> ", // prompt + true); // echo input + + if (err.Success()) + Debugger::GetSharedInstance().PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +bool +ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def) +{ + // Convert StringList to one long, newline delimited, const char *. + std::string function_def_string; + + int num_lines = function_def.GetSize(); + + for (int i = 0; i < num_lines; ++i) + { + function_def_string.append (function_def.GetStringAtIndex(i)); + if (function_def_string.at (function_def_string.length() - 1) != '\n') + function_def_string.append ("\n"); + + } + + return ExecuteMultipleLines (function_def_string.c_str()); +} + +bool +ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user_input, StringList &callback_data) +{ + static int num_created_functions = 0; + + user_input.RemoveBlankLines (); + int num_lines = user_input.GetSize(); + std::string last_function_call; + + // Go through lines of input looking for any function definitions. For each function definition found, + // export the function definition to Python, create a potential function call for the function, and + // mark the lines of the function to be removed from the user input. + + for (int i = 0; i < num_lines; ++i) + { + int function_start = i; + std::string current_str = user_input.GetStringAtIndex (i); + const char *current_line = current_str.c_str(); + int len = 0; + if (current_line) + len = strlen (current_line); + + // Check to see if the current line is the start of a Python function definition. + if (len > 4 && strncmp (current_line, "def ", 4) == 0) + { + // We've found the first line of a function. First, get the function name. + + // Skip over the 'def '. + char *start = (char *) current_line + 4; + + // Skip over white space. + while (start[0] == ' ' || start[0] == '\t') + ++start; + + // Find the end of the function name. + char *end = start; + while (isalnum (end[0]) || end[0] == '_') + ++end; + + int name_len = end - start; + std::string func_name = current_str.substr (4, name_len); + + // Now to find the last line of the function. That will be the first line that does not begin with + // any white space (thanks to Python's indentation rules). + ++i; + bool found = false; + while (i < num_lines && !found) + { + std::string next_str = user_input.GetStringAtIndex (i); + const char *next_line = next_str.c_str(); + if (next_line[0] != ' ' && next_line[0] != '\t') + found = true; + else + ++i; + } + if (found) + --i; // Make 'i' correspond to the last line of the function. + int function_end = i; + + // Special case: All of user_input is one big function definition. + if ((function_start == 0) && (function_end == (num_lines - 1))) + { + ExportFunctionDefinitionToInterpreter (user_input); + last_function_call = func_name + " ()"; + callback_data.AppendString (last_function_call.c_str()); + return callback_data.GetSize() > 0; + } + else + { + // Make a copy of the function definition: + StringList new_function; + for (int k = function_start; k <= function_end; ++k) + { + new_function.AppendString (user_input.GetStringAtIndex (k)); + // Mark the string to be deleted from user_input. + user_input.DeleteStringAtIndex (k); + user_input.InsertStringAtIndex (k, "<lldb_delete>"); + } + ExportFunctionDefinitionToInterpreter (new_function); + last_function_call = func_name + " ()"; + } + } + } + + // Now instead of trying to really delete the marked lines from user_input, we will just copy all the + // unmarked lines into a new StringList. + + StringList new_user_input; + + for (int i = 0; i < num_lines; ++i) + { + std::string current_string = user_input.GetStringAtIndex (i); + if (current_string.compare (0, 13, "<lldb_delete>") == 0) + continue; + + new_user_input.AppendString (current_string.c_str()); + } + + num_lines = new_user_input.GetSize(); + + if (num_lines > 0) + { + if (num_lines == 1 + && strchr (new_user_input.GetStringAtIndex(0), '\n') == NULL) + { + // If there's only one line of input, and it doesn't contain any newline characters.... + callback_data.AppendString (new_user_input.GetStringAtIndex (0)); + } + else + { + // Create the new function name. + StreamString func_name; + func_name.Printf ("lldb_bp_callback_func_%d", num_created_functions); + //std::string func_name = "lldb_bp_callback_func_" + num_created_functions; + ++num_created_functions; + + // Create the function call for the new function. + last_function_call = func_name.GetString() + " ()"; + + // Create the Python function definition line (which will have to be inserted at the beginning of + // the function). + std::string def_line = "def " + func_name.GetString() + " ():"; + + + // Indent all lines an additional four spaces (as they are now being put inside a function definition). + for (int i = 0; i < num_lines; ++i) + { + const char *temp_cstring = new_user_input.GetStringAtIndex(i); + std::string temp2 = " "; + temp2.append(temp_cstring); + new_user_input.DeleteStringAtIndex (i); + new_user_input.InsertStringAtIndex (i, temp2.c_str()); + } + + // Insert the function definition line at the top of the new function. + new_user_input.InsertStringAtIndex (0, def_line.c_str()); + + ExportFunctionDefinitionToInterpreter (new_user_input); + callback_data.AppendString (last_function_call.c_str()); + } + } + else + { + if (!last_function_call.empty()) + callback_data.AppendString (last_function_call.c_str()); + } + + return callback_data.GetSize() > 0; +} + +bool +ScriptInterpreterPython::BreakpointCallbackFunction +( + void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id +) +{ + bool ret_value = true; + bool temp_bool; + + BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton; + + const char *python_string = bp_option_data->script_source.GetStringAtIndex(0); + + if (python_string != NULL) + { + bool success = + Debugger::GetSharedInstance().GetCommandInterpreter().GetScriptInterpreter()->ExecuteOneLineWithReturn + (python_string, + ScriptInterpreter::eBool, + (void *) &temp_bool); + if (success) + ret_value = temp_bool; + } + + return ret_value; +} diff --git a/lldb/source/Interpreter/StateVariable.cpp b/lldb/source/Interpreter/StateVariable.cpp new file mode 100644 index 00000000000..3f3c3fdb1e0 --- /dev/null +++ b/lldb/source/Interpreter/StateVariable.cpp @@ -0,0 +1,320 @@ +//===-- StateVariable.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" + + +#include "lldb/Interpreter/StateVariable.h" + +using namespace lldb; +using namespace lldb_private; + +// Variables with integer values. + +StateVariable::StateVariable +( + const char *name, + int value, + const char *help, + Callback func_ptr +) : + m_name (name), + m_type (eTypeInteger), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_int_value = value; +} + +// Variables with boolean values. + +StateVariable::StateVariable +( + const char *name, + bool value, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeBoolean), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_int_value = value; +} + +// Variables with string values. + +StateVariable::StateVariable +( + const char *name, + const char *value, + bool can_append, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeString), + m_int_value (0), + m_string_values (), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_string_values.AppendArgument(value); +} + +// Variables with array of strings values. + +StateVariable::StateVariable +( + const char *name, + const Args *args, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeStringArray), + m_help_text (help), + m_string_values(), + m_verification_func_ptr (func_ptr) +{ + if (args) + m_string_values = *args; +} + +StateVariable::~StateVariable () +{ +} + +const char * +StateVariable::GetName () const +{ + return m_name.c_str(); +} + +StateVariable::Type +StateVariable::GetType () const +{ + return m_type; +} + +int +StateVariable::GetIntValue () const +{ + return m_int_value; +} + +bool +StateVariable::GetBoolValue () const +{ + return m_int_value; +} + +const char * +StateVariable::GetStringValue () const +{ + return m_string_values.GetArgumentAtIndex(0); +} + +const Args & +StateVariable::GetArgs () const +{ + return m_string_values; +} + +Args & +StateVariable::GetArgs () +{ + return m_string_values; +} + +const char * +StateVariable::GetHelp () const +{ + return m_help_text.c_str(); +} + +void +StateVariable::SetHelp (const char *help) +{ + m_help_text = help; +} + +void +StateVariable::AppendVariableInformation (CommandReturnObject &result) +{ + switch (m_type) + { + case eTypeBoolean: + if (m_int_value) + result.AppendMessageWithFormat (" %s (bool) = True\n", m_name.c_str()); + else + result.AppendMessageWithFormat (" %s (bool) = False\n", m_name.c_str()); + break; + + case eTypeInteger: + result.AppendMessageWithFormat (" %s (int) = %d\n", m_name.c_str(), m_int_value); + break; + + case eTypeString: + { + const char *cstr = m_string_values.GetArgumentAtIndex(0); + if (cstr && cstr[0]) + result.AppendMessageWithFormat (" %s (str) = '%s'\n", m_name.c_str(), cstr); + else + result.AppendMessageWithFormat (" %s (str) = <no value>\n", m_name.c_str()); + } + break; + + case eTypeStringArray: + { + const size_t argc = m_string_values.GetArgumentCount(); + result.AppendMessageWithFormat (" %s (string vector):\n", m_name.c_str()); + for (size_t i = 0; i < argc; ++i) + result.AppendMessageWithFormat (" [%d] %s\n", i, m_string_values.GetArgumentAtIndex(i)); + } + break; + + default: + break; + } +} + +void +StateVariable::SetStringValue (const char *new_value) +{ + if (m_string_values.GetArgumentCount() > 0) + m_string_values.ReplaceArgumentAtIndex(0, new_value); + else + m_string_values.AppendArgument(new_value); +} + +void +StateVariable::SetIntValue (int new_value) +{ + m_int_value = new_value; +} + +void +StateVariable::SetBoolValue (bool new_value) +{ + m_int_value = new_value; +} + +void +StateVariable::AppendStringValue (const char *cstr) +{ + if (cstr && cstr[0]) + { + if (m_string_values.GetArgumentCount() == 0) + { + m_string_values.AppendArgument(cstr); + } + else + { + const char *curr_arg = m_string_values.GetArgumentAtIndex(0); + if (curr_arg != NULL) + { + std::string new_arg_str(curr_arg); + new_arg_str += " "; + new_arg_str += cstr; + m_string_values.ReplaceArgumentAtIndex(0, new_arg_str.c_str()); + } + else + { + m_string_values.ReplaceArgumentAtIndex(0, cstr); + } + } + } +} + +bool +StateVariable::VerifyValue (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + return (*m_verification_func_ptr) (interpreter, data, result); +} + +//void +//StateVariable::SetArrayValue (STLStringArray &new_value) +//{ +// m_string_values.AppendArgument.append(cstr); +// +// if (m_array_value != NULL) +// { +// if (m_array_value->size() > 0) +// { +// m_array_value->clear(); +// } +// } +// else +// m_array_value = new STLStringArray; +// +// for (int i = 0; i < new_value.size(); ++i) +// m_array_value->push_back (new_value[i]); +//} +// + +void +StateVariable::ArrayClearValues () +{ + m_string_values.Clear(); +} + + +void +StateVariable::ArrayAppendValue (const char *cstr) +{ + m_string_values.AppendArgument(cstr); +} + + +bool +StateVariable::HasVerifyFunction () +{ + return (m_verification_func_ptr != NULL); +} + +// Verification functions for various command interpreter variables. + +bool +StateVariable::VerifyScriptLanguage (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + bool valid_lang = true; + interpreter->SetScriptLanguage (Args::StringToScriptLanguage((char *) data, eScriptLanguageDefault, &valid_lang)); + return valid_lang; +} + +bool +StateVariable::BroadcastPromptChange (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + char *prompt = (char *) data; + if (prompt != NULL) + { + std::string tmp_prompt = prompt ; + int len = tmp_prompt.size(); + if (len > 1 + && (tmp_prompt[0] == '\'' || tmp_prompt[0] == '"') + && (tmp_prompt[len-1] == tmp_prompt[0])) + { + tmp_prompt = tmp_prompt.substr(1,len-2); + } + len = tmp_prompt.size(); + if (tmp_prompt[len-1] != ' ') + tmp_prompt.append(" "); + strcpy (prompt, tmp_prompt.c_str()); + data = (void *) prompt; + } + EventSP new_event_sp; + new_event_sp.reset (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (prompt))); + interpreter->BroadcastEvent (new_event_sp); + + return true; +} + diff --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py new file mode 100644 index 00000000000..38b2f9ef01e --- /dev/null +++ b/lldb/source/Interpreter/embedded_interpreter.py @@ -0,0 +1,90 @@ +import readline +import code +import sys +import traceback + +class SimpleREPL(code.InteractiveConsole): + def __init__(self, prompt, dict): + code.InteractiveConsole.__init__(self,dict) + self.prompt = prompt + self.loop_exit = False + self.dict = dict + + def interact(self): + try: + sys.ps1 + except AttributeError: + sys.ps1 = ">>> " + try: + sys.ps2 + except AttributeError: + sys.ps2 = "... " + + while not self.loop_exit: + try: + self.read_py_command() + except (SystemExit, EOFError): + # EOF while in Python just breaks out to top level. + self.write('\n') + self.loop_exit = True + break + except KeyboardInterrupt: + self.write("\nKeyboardInterrupt\n") + self.resetbuffer() + more = 0 + except: + traceback.print_exc() + + def process_input (self, in_str): + # Canonicalize the format of the input string + temp_str = in_str + temp_str.strip(' \t') + words = temp_str.split() + temp_str = ('').join(words) + + # Check the input string to see if it was the quit + # command. If so, intercept it, so that it doesn't + # close stdin on us! + if (temp_str.lower() == "quit()"): + self.loop_exit = True + in_str = "raise SystemExit " + return in_str + + def my_raw_input (self, prompt): + stream = sys.stdout + stream.write (prompt) + stream.flush () + try: + line = sys.stdin.readline() + except KeyboardInterrupt: + line = " \n" + except (SystemExit, EOFError): + line = "quit()\n" + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line + + def read_py_command(self): + # Read off a complete Python command. + more = 0 + while 1: + if more: + prompt = sys.ps2 + else: + prompt = sys.ps1 + line = self.my_raw_input(prompt) + # Can be None if sys.stdin was redefined + encoding = getattr(sys.stdin, "encoding", None) + if encoding and not isinstance(line, unicode): + line = line.decode(encoding) + line = self.process_input (line) + more = self.push(line) + if not more: + break + +def run_python_interpreter (dict): + # Pass in the dictionary, for continuity from one session to the next. + repl = SimpleREPL('>>> ', dict) + repl.interact() |