From 6f79bb2d57218e2bd11bf26876d49a1276328bd3 Mon Sep 17 00:00:00 2001 From: Enrico Granata Date: Fri, 13 Mar 2015 22:22:28 +0000 Subject: [PATCH] Add support for Python object commands to return custom short and long help by implementing def get_short_help(self) def get_long_help(self) methods on the command object Also, add a test case for this feature llvm-svn: 232224 --- lldb/include/lldb/Interpreter/CommandObject.h | 5 +- lldb/include/lldb/Interpreter/ScriptInterpreter.h | 16 +++ .../lldb/Interpreter/ScriptInterpreterPython.h | 8 ++ lldb/source/Commands/CommandObjectCommands.cpp | 34 ++++- lldb/source/Interpreter/CommandObject.cpp | 6 + .../source/Interpreter/ScriptInterpreterPython.cpp | 150 +++++++++++++++++++++ .../command_script/TestCommandScript.py | 2 +- lldb/test/functionalities/command_script/py_import | 2 +- .../test/functionalities/command_script/welcome.py | 17 ++- 9 files changed, 229 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h index bace3264..541a6ef 100644 --- a/lldb/include/lldb/Interpreter/CommandObject.h +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -98,7 +98,7 @@ public: return m_interpreter; } - const char * + virtual const char * GetHelp (); virtual const char * @@ -114,6 +114,9 @@ public: SetHelp (const char * str); void + SetHelp (std::string str); + + void SetHelpLong (const char * str); void diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 213a300..386919e 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -614,6 +614,22 @@ public: } virtual bool + GetShortHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) + { + dest.clear(); + return false; + } + + virtual bool + GetLongHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) + { + dest.clear(); + return false; + } + + virtual bool CheckObjectExists (const char* name) { return false; diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h index a959d25..6dc03618 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -214,6 +214,14 @@ public: GetDocumentationForItem (const char* item, std::string& dest) override; bool + GetShortHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) override; + + bool + GetLongHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) override ; + + bool CheckObjectExists (const char* name) override { if (!name || !name[0]) diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index febbfa8..92e8670 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -1435,6 +1435,8 @@ class CommandObjectScriptingObject : public CommandObjectRaw private: lldb::ScriptInterpreterObjectSP m_cmd_obj_sp; ScriptedCommandSynchronicity m_synchro; + bool m_fetched_help_short:1; + bool m_fetched_help_long:1; public: @@ -1447,7 +1449,9 @@ public: NULL, NULL), m_cmd_obj_sp(cmd_obj_sp), - m_synchro(synch) + m_synchro(synch), + m_fetched_help_short(false), + m_fetched_help_long(false) { StreamString stream; stream.Printf("For more information run 'help %s'",name.c_str()); @@ -1476,10 +1480,38 @@ public: { return m_synchro; } + + virtual const char * + GetHelp () + { + if (!m_fetched_help_short) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + if (scripter) + { + std::string docstring; + m_fetched_help_short = scripter->GetShortHelpForCommandObject(m_cmd_obj_sp,docstring); + if (!docstring.empty()) + SetHelp(docstring); + } + } + return CommandObjectRaw::GetHelp(); + } virtual const char * GetHelpLong () { + if (!m_fetched_help_long) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + if (scripter) + { + std::string docstring; + m_fetched_help_long = scripter->GetLongHelpForCommandObject(m_cmd_obj_sp,docstring); + if (!docstring.empty()) + SetHelpLong(docstring); + } + } return CommandObjectRaw::GetHelpLong(); } diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 021442e..5de3e9d 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -124,6 +124,12 @@ CommandObject::SetHelp (const char *cstr) } void +CommandObject::SetHelp (std::string str) +{ + m_cmd_help_short = str; +} + +void CommandObject::SetHelpLong (const char *cstr) { m_cmd_help_long = cstr; diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index aab837c..4ef0bbe 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -2793,6 +2793,156 @@ ScriptInterpreterPython::GetDocumentationForItem(const char* item, std::string& } } +bool +ScriptInterpreterPython::GetShortHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) +{ + bool got_string = false; + dest.clear(); + + Locker py_lock (this, + Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + static char callee_name[] = "get_short_help"; + + if (!cmd_obj_sp) + return false; + + PyObject* implementor = (PyObject*)cmd_obj_sp->GetObject(); + + if (implementor == nullptr || implementor == Py_None) + return false; + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == nullptr || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return false; + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return false; + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, nullptr); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (py_return != nullptr && py_return != Py_None) + { + if (PyString_Check(py_return)) + { + dest.assign(PyString_AsString(py_return)); + got_string = true; + } + } + Py_XDECREF(py_return); + + return got_string; +} + +bool +ScriptInterpreterPython::GetLongHelpForCommandObject (lldb::ScriptInterpreterObjectSP cmd_obj_sp, + std::string& dest) +{ + bool got_string = false; + dest.clear(); + + Locker py_lock (this, + Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + static char callee_name[] = "get_long_help"; + + if (!cmd_obj_sp) + return false; + + PyObject* implementor = (PyObject*)cmd_obj_sp->GetObject(); + + if (implementor == nullptr || implementor == Py_None) + return false; + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == nullptr || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return false; + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return false; + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, nullptr); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (py_return != nullptr && py_return != Py_None) + { + if (PyString_Check(py_return)) + { + dest.assign(PyString_AsString(py_return)); + got_string = true; + } + } + Py_XDECREF(py_return); + + return got_string; +} + std::unique_ptr ScriptInterpreterPython::AcquireInterpreterLock () { diff --git a/lldb/test/functionalities/command_script/TestCommandScript.py b/lldb/test/functionalities/command_script/TestCommandScript.py index 9c1183a..b7e466d 100644 --- a/lldb/test/functionalities/command_script/TestCommandScript.py +++ b/lldb/test/functionalities/command_script/TestCommandScript.py @@ -119,7 +119,7 @@ class CmdPythonTestCase(TestBase): self.runCmd("command script clear") # Test that re-defining an existing command works - self.runCmd('command script add my_command --function welcome.welcome_impl') + self.runCmd('command script add my_command --class welcome.WelcomeCommand') self.expect('my_command Blah', substrs = ['Hello Blah, welcome to LLDB']) self.runCmd('command script add my_command --function welcome.target_name_impl') diff --git a/lldb/test/functionalities/command_script/py_import b/lldb/test/functionalities/command_script/py_import index afc8c82..6150e02 100644 --- a/lldb/test/functionalities/command_script/py_import +++ b/lldb/test/functionalities/command_script/py_import @@ -2,7 +2,7 @@ script import sys, os script sys.path.append(os.path.join(os.getcwd(), os.pardir)) script import welcome script import bug11569 -command script add welcome --function welcome.welcome_impl +command script add welcome --class welcome.WelcomeCommand command script add targetname --function welcome.target_name_impl command script add longwait --function welcome.print_wait_impl command script import mysto.py --allow-reload diff --git a/lldb/test/functionalities/command_script/welcome.py b/lldb/test/functionalities/command_script/welcome.py index c444934..90bd0b8 100644 --- a/lldb/test/functionalities/command_script/welcome.py +++ b/lldb/test/functionalities/command_script/welcome.py @@ -1,12 +1,15 @@ import sys -def welcome_impl(debugger, args, result, dict): - """ - Just a docstring for welcome_impl - A command that says hello to LLDB users - """ - print >>result, ('Hello ' + args + ', welcome to LLDB'); - return None; +class WelcomeCommand(object): + def __init__(self, debugger, session_dict): + pass + + def get_short_help(self): + return "Just a docstring for welcome_impl\nA command that says hello to LLDB users" + + def __call__(self, debugger, args, exe_ctx, result): + print >>result, ('Hello ' + args + ', welcome to LLDB'); + return None; def target_name_impl(debugger, args, result, dict): target = debugger.GetSelectedTarget() -- 2.7.4