Python commands:
It is now possible to use 'command alias --python' to define a command name that actually triggers execution of a Python function
(e.g. command alias --python foo foo_impl makes a command named 'foo' that runs Python function 'foo_impl')
The Python function foo_impl should have as signature: def foo_impl(debugger, args, stream, dict): where
debugger is an object wrapping an LLDB SBDebugger
args is the command line arguments, as an unparsed Python string
stream is an SBStream that represents the standard output
dict is an internal utility parameter and should be left untouched
The function should return None on no error, or an error string to describe any problems
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@137722 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp
index 779a4ab..09ac12b 100644
--- a/source/Commands/CommandObjectCommands.cpp
+++ b/source/Commands/CommandObjectCommands.cpp
@@ -15,8 +15,11 @@
#include "llvm/ADT/StringRef.h"
// Project includes
+#include "CommandObjectPythonFunction.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/InputReader.h"
+#include "lldb/Core/InputReaderEZ.h"
+#include "lldb/Core/StringList.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandObjectRegexCommand.h"
@@ -306,8 +309,131 @@
// CommandObjectCommandsAlias
//-------------------------------------------------------------------------
+static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python function with this signature:\n"
+ "def my_command_impl(debugger, args, stream, dict):";
+
+
class CommandObjectCommandsAlias : public CommandObject
{
+
+ class PythonAliasReader : public InputReaderEZ
+ {
+ private:
+ CommandInterpreter& m_interpreter;
+ std::string m_cmd_name;
+ StringList m_user_input;
+ DISALLOW_COPY_AND_ASSIGN (PythonAliasReader);
+ public:
+ PythonAliasReader(Debugger& debugger,
+ CommandInterpreter& interpreter,
+ std::string cmd_name) :
+ InputReaderEZ(debugger),
+ m_interpreter(interpreter),
+ m_cmd_name(cmd_name),
+ m_user_input()
+ {}
+
+ virtual
+ ~PythonAliasReader()
+ {
+ }
+
+ virtual void ActivateHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (!batch_mode)
+ {
+ out_stream->Printf ("%s\n", g_python_command_instructions);
+ if (data.reader.GetPrompt())
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+
+ virtual void ReactivateHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (data.reader.GetPrompt() && !batch_mode)
+ {
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+ virtual void GotTokenHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (data.bytes && data.bytes_len)
+ {
+ m_user_input.AppendString(data.bytes, data.bytes_len);
+ }
+ if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode)
+ {
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+ virtual void InterruptHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ data.reader.SetIsDone (true);
+ if (!batch_mode)
+ {
+ out_stream->Printf ("Warning: No command attached to breakpoint.\n");
+ out_stream->Flush();
+ }
+ }
+ virtual void EOFHandler(HandlerData& data)
+ {
+ data.reader.SetIsDone (true);
+ }
+ virtual void DoneHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+
+ ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
+ if (!interpreter)
+ {
+ out_stream->Printf ("Internal error #1: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ StringList funct_name_sl;
+ if (!interpreter->GenerateScriptAliasFunction (m_user_input,
+ funct_name_sl))
+ {
+ out_stream->Printf ("Internal error #2: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ if (funct_name_sl.GetSize() == 0)
+ {
+ out_stream->Printf ("Internal error #3: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ const char *funct_name = funct_name_sl.GetStringAtIndex(0);
+ if (!funct_name || !funct_name[0])
+ {
+ out_stream->Printf ("Internal error #4: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+
+ // everything should be fine now, let's add this alias
+
+ CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter,
+ m_cmd_name,
+ funct_name));
+
+ m_interpreter.AddAlias(m_cmd_name.c_str(), command_obj_sp);
+ }
+ };
+
public:
CommandObjectCommandsAlias (CommandInterpreter &interpreter) :
CommandObject (interpreter,
@@ -425,6 +551,98 @@
// Get the alias command.
const std::string alias_command = args.GetArgumentAtIndex (0);
+
+ if (
+ (strcmp("--python",alias_command.c_str()) == 0) ||
+ (strcmp("-P",alias_command.c_str()) == 0)
+ )
+ {
+
+ if (argc < 3)
+ {
+ // this is a definition of the form
+ // command alias --python foo_cmd
+ // and the user will type foo_cmd_impl by hand
+ std::string cmd_name = args.GetArgumentAtIndex(1);
+ // Verify that the command is alias-able.
+ if (m_interpreter.CommandExists (cmd_name.c_str()))
+ {
+ result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n",
+ cmd_name.c_str());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ if (m_interpreter.AliasExists (cmd_name.c_str())
+ || m_interpreter.UserCommandExists (cmd_name.c_str()))
+ {
+ result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n",
+ cmd_name.c_str());
+ }
+
+
+ InputReaderSP reader_sp (new PythonAliasReader (m_interpreter.GetDebugger(),
+ m_interpreter,
+ cmd_name));
+
+ if (reader_sp)
+ {
+
+ InputReaderEZ::InitializationParameters ipr;
+
+ Error err (reader_sp->Initialize (ipr.SetBaton(NULL).SetPrompt(" ")));
+ if (err.Success())
+ {
+ m_interpreter.GetDebugger().PushInputReader (reader_sp);
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ else
+ {
+ result.AppendError (err.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ else
+ {
+ result.AppendError("out of memory");
+ result.SetStatus (eReturnStatusFailed);
+ }
+
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+ else
+ {
+ // this is a definition of the form
+ // command alias --python foo_cmd funct_impl_foo
+ std::string cmd_name = args.GetArgumentAtIndex(1);
+ std::string funct_name = args.GetArgumentAtIndex(2);
+
+ // Verify that the command is alias-able.
+ if (m_interpreter.CommandExists (cmd_name.c_str()))
+ {
+ result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n",
+ cmd_name.c_str());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter,
+ cmd_name,
+ funct_name));
+
+ if (m_interpreter.AliasExists (cmd_name.c_str())
+ || m_interpreter.UserCommandExists (cmd_name.c_str()))
+ {
+ result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n",
+ cmd_name.c_str());
+ }
+
+ m_interpreter.AddAlias(cmd_name.c_str(), command_obj_sp);
+
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+ }
// Strip the new alias name off 'raw_command_string' (leave it on args, which gets passed to 'Execute', which
// does the stripping itself.
diff --git a/source/Commands/CommandObjectPythonFunction.cpp b/source/Commands/CommandObjectPythonFunction.cpp
new file mode 100644
index 0000000..b1bb2af
--- /dev/null
+++ b/source/Commands/CommandObjectPythonFunction.cpp
@@ -0,0 +1,88 @@
+//===-- CommandObjectPythonFunction.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectPythonFunction.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+#include "lldb/API/SBStream.h"
+
+#include "lldb/Core/Debugger.h"
+
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Interpreter/Options.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;
+
+//-------------------------------------------------------------------------
+// CommandObjectApropos
+//-------------------------------------------------------------------------
+
+CommandObjectPythonFunction::CommandObjectPythonFunction (CommandInterpreter &interpreter,
+ std::string name,
+ std::string funct) :
+ CommandObject (interpreter,
+ name.c_str(),
+ (std::string("Run Python function ") + funct).c_str(),
+ NULL),
+ m_function_name(funct)
+{
+ CommandArgumentEntry arg;
+ CommandArgumentData search_word_arg;
+
+ // Define the first (and only) variant of this arg.
+ search_word_arg.arg_type = eArgTypeSearchWord;
+ search_word_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the argument entry.
+ arg.push_back (search_word_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back (arg);
+}
+
+CommandObjectPythonFunction::~CommandObjectPythonFunction()
+{
+}
+
+bool
+CommandObjectPythonFunction::ExecuteRawCommandString (const char *raw_command_line,
+ CommandReturnObject &result)
+{
+ ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter();
+
+ Error error;
+
+ lldb::SBStream stream;
+
+ if (scripter->RunScriptBasedCommand(m_function_name.c_str(),
+ raw_command_line,
+ stream,
+ error) == false)
+ {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ else
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ result.GetOutputStream() << stream.GetData();
+
+ return result.Succeeded();
+}
diff --git a/source/Commands/CommandObjectPythonFunction.h b/source/Commands/CommandObjectPythonFunction.h
new file mode 100644
index 0000000..4af857d
--- /dev/null
+++ b/source/Commands/CommandObjectPythonFunction.h
@@ -0,0 +1,62 @@
+//===-- CommandObjectPythonFunction.h -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectPythonFunction_h_
+#define liblldb_CommandObjectPythonFunction_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+//-------------------------------------------------------------------------
+// CommandObjectApropos
+//-------------------------------------------------------------------------
+
+class CommandObjectPythonFunction : public CommandObject
+{
+private:
+ std::string m_function_name;
+
+public:
+
+ CommandObjectPythonFunction (CommandInterpreter &interpreter,
+ std::string name,
+ std::string funct);
+
+ virtual
+ ~CommandObjectPythonFunction ();
+
+ virtual bool
+ ExecuteRawCommandString (const char *raw_command_line, CommandReturnObject &result);
+
+ virtual bool
+ WantsRawCommandString ()
+ {
+ return true;
+ }
+
+ bool
+ Execute (Args& command,
+ CommandReturnObject &result)
+ {
+ std::string cmd_string;
+ command.GetCommandString(cmd_string);
+ return ExecuteRawCommandString(cmd_string.c_str(), result);
+ }
+
+
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectPythonFunction_h_