this patch introduces a new command script import command which takes as input a filename for a Python script and imports the module contained in that file. the containing directory is added to the Python path such that dependencies are honored. also, the module may contain an __lldb_init_module(debugger,dict) function, which gets called after importing, and which can somehow initialize the module's interaction with lldb

git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@142283 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/API/SBCommandInterpreter.cpp b/source/API/SBCommandInterpreter.cpp
index 8a15423..ec2653d 100644
--- a/source/API/SBCommandInterpreter.cpp
+++ b/source/API/SBCommandInterpreter.cpp
@@ -358,6 +358,13 @@
     lldb_private::CommandReturnObject& cmd_retobj
 );
 
+extern "C" bool           LLDBSwigPythonCallModuleInit 
+(
+    const std::string python_module_name,
+    const char *session_dictionary_name,
+    lldb::DebuggerSP& debugger
+);
+
 
 extern "C" void init_lldb(void);
 
@@ -377,6 +384,7 @@
                                                   LLDBSwigPython_GetIndexOfChildWithName,
                                                   LLDBSWIGPython_CastPyObjectToSBValue,
                                                   LLDBSwigPython_UpdateSynthProviderInstance,
-                                                  LLDBSwigPythonCallCommand);
+                                                  LLDBSwigPythonCallCommand,
+                                                  LLDBSwigPythonCallModuleInit);
     }
 }
diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp
index da0af98..ba81f28 100644
--- a/source/Commands/CommandObjectCommands.cpp
+++ b/source/Commands/CommandObjectCommands.cpp
@@ -1220,6 +1220,78 @@
     
 };
 
+//-------------------------------------------------------------------------
+// CommandObjectCommandsScriptImport
+//-------------------------------------------------------------------------
+
+class CommandObjectCommandsScriptImport : public CommandObject
+{
+public:
+    CommandObjectCommandsScriptImport (CommandInterpreter &interpreter) :
+    CommandObject (interpreter,
+                   "command script import",
+                   "Import a scripting module in LLDB.",
+                   NULL)
+    {
+        CommandArgumentEntry arg1;
+        CommandArgumentData cmd_arg;
+        
+        // Define the first (and only) variant of this arg.
+        cmd_arg.arg_type = eArgTypePath;
+        cmd_arg.arg_repetition = eArgRepeatPlain;
+        
+        // There is only one variant this argument could be; put it into the argument entry.
+        arg1.push_back (cmd_arg);
+        
+        // Push the data for the first argument into the m_arguments vector.
+        m_arguments.push_back (arg1);
+    }
+    
+    ~CommandObjectCommandsScriptImport ()
+    {
+    }
+    
+    bool
+    Execute
+    (
+     Args& args,
+     CommandReturnObject &result
+     )
+    {
+        
+        if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython)
+        {
+            result.AppendError ("only scripting language supported for module importing is currently Python");
+            result.SetStatus (eReturnStatusFailed);
+            return false;
+        }
+        
+        size_t argc = args.GetArgumentCount();
+        
+        if (argc != 1)
+        {
+            result.AppendError ("'command script import' requires one argument");
+            result.SetStatus (eReturnStatusFailed);
+            return false;
+        }
+        
+        std::string path = args.GetArgumentAtIndex(0);
+        Error error;
+        
+        if (m_interpreter.GetScriptInterpreter()->LoadScriptingModule(path.c_str(),
+                                                                      error))
+        {
+            result.SetStatus (eReturnStatusSuccessFinishNoResult);
+        }
+        else
+        {
+            result.AppendErrorWithFormat("module importing failed: %s", error.AsCString());
+            result.SetStatus (eReturnStatusFailed);
+        }
+        
+        return result.Succeeded();
+    }
+};
 
 //-------------------------------------------------------------------------
 // CommandObjectCommandsScriptAdd
@@ -1684,6 +1756,7 @@
         LoadSubCommand ("delete",   CommandObjectSP (new CommandObjectCommandsScriptDelete (interpreter)));
         LoadSubCommand ("clear", CommandObjectSP (new CommandObjectCommandsScriptClear (interpreter)));
         LoadSubCommand ("list",   CommandObjectSP (new CommandObjectCommandsScriptList (interpreter)));
+        LoadSubCommand ("import",   CommandObjectSP (new CommandObjectCommandsScriptImport (interpreter)));
     }
 
     ~CommandObjectMultiwordCommandsScript ()
diff --git a/source/Host/common/FileSpec.cpp b/source/Host/common/FileSpec.cpp
index 5e034db..35e12eb 100644
--- a/source/Host/common/FileSpec.cpp
+++ b/source/Host/common/FileSpec.cpp
@@ -699,10 +699,39 @@
             return ::snprintf (path, path_max_len, "%s", filename);
         }
     }
-    path[0] = '\0';
+    if (path)
+        path[0] = '\0';
     return 0;
 }
 
+ConstString
+FileSpec::GetFileNameExtension () const
+{
+    const char *filename = m_filename.GetCString();
+    if (filename == NULL)
+        return ConstString();
+    
+    char* dot_pos = strrchr(filename, '.');
+    if (dot_pos == NULL)
+        return ConstString();
+    
+    return ConstString(dot_pos+1);
+}
+
+ConstString
+FileSpec::GetFileNameStrippingExtension () const
+{
+    const char *filename = m_filename.GetCString();
+    if (filename == NULL)
+        return ConstString();
+    
+    char* dot_pos = strrchr(filename, '.');
+    if (dot_pos == NULL)
+        return m_filename;
+    
+    return ConstString(filename, dot_pos-filename);
+}
+
 //------------------------------------------------------------------
 // Returns a shared pointer to a data buffer that contains all or
 // part of the contents of a file. The data is memory mapped and
diff --git a/source/Interpreter/ScriptInterpreter.cpp b/source/Interpreter/ScriptInterpreter.cpp
index 27bbb67..e125d65 100644
--- a/source/Interpreter/ScriptInterpreter.cpp
+++ b/source/Interpreter/ScriptInterpreter.cpp
@@ -100,7 +100,8 @@
                                           SWIGPythonGetIndexOfChildWithName python_swig_get_index_child,
                                           SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue,
                                           SWIGPythonUpdateSynthProviderInstance python_swig_update_provider,
-                                          SWIGPythonCallCommand python_swig_call_command)
+                                          SWIGPythonCallCommand python_swig_call_command,
+                                          SWIGPythonCallModuleInit python_swig_call_mod_init)
 {
     ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback, 
                                                     python_swig_breakpoint_callback,
@@ -111,7 +112,8 @@
                                                     python_swig_get_index_child,
                                                     python_swig_cast_to_sbvalue,
                                                     python_swig_update_provider,
-                                                    python_swig_call_command);
+                                                    python_swig_call_command,
+                                                    python_swig_call_mod_init);
 }
 
 void
diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp
index 634f76d..f1f4a32 100644
--- a/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/source/Interpreter/ScriptInterpreterPython.cpp
@@ -42,6 +42,7 @@
 static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue  = NULL;
 static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = NULL;
 static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = NULL;
+static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = NULL;
 
 static int
 _check_and_flush (FILE *stream)
@@ -199,6 +200,10 @@
     PyRun_SimpleString (run_string.GetData());
 
     run_string.Clear();
+    run_string.Printf ("run_one_line (%s, 'import os')", m_dictionary_name.c_str());
+    PyRun_SimpleString (run_string.GetData());
+    
+    run_string.Clear();
     run_string.Printf ("run_one_line (%s, 'lldb.debugger_unique_id = %d')", m_dictionary_name.c_str(),
                        interpreter.GetDebugger().GetID());
     PyRun_SimpleString (run_string.GetData());
@@ -702,7 +707,7 @@
 
 bool
 ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string,
-                                                   ScriptInterpreter::ReturnType return_type,
+                                                   ScriptInterpreter::ScriptReturnType return_type,
                                                    void *ret_value)
 {
 
@@ -783,85 +788,85 @@
         {
             switch (return_type)
             {
-                case eCharPtr: // "char *"
+                case eScriptReturnTypeCharPtr: // "char *"
                 {
                     const char format[3] = "s#";
                     success = PyArg_Parse (py_return, format, (char **) ret_value);
                     break;
                 }
-                case eCharStrOrNone: // char* or NULL if py_return == Py_None
+                case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return == Py_None
                 {
                     const char format[3] = "z";
                     success = PyArg_Parse (py_return, format, (char **) ret_value);
                     break;
                 }
-                case eBool:
+                case eScriptReturnTypeBool:
                 {
                     const char format[2] = "b";
                     success = PyArg_Parse (py_return, format, (bool *) ret_value);
                     break;
                 }
-                case eShortInt:
+                case eScriptReturnTypeShortInt:
                 {
                     const char format[2] = "h";
                     success = PyArg_Parse (py_return, format, (short *) ret_value);
                     break;
                 }
-                case eShortIntUnsigned:
+                case eScriptReturnTypeShortIntUnsigned:
                 {
                     const char format[2] = "H";
                     success = PyArg_Parse (py_return, format, (unsigned short *) ret_value);
                     break;
                 }
-                case eInt:
+                case eScriptReturnTypeInt:
                 {
                     const char format[2] = "i";
                     success = PyArg_Parse (py_return, format, (int *) ret_value);
                     break;
                 }
-                case eIntUnsigned:
+                case eScriptReturnTypeIntUnsigned:
                 {
                     const char format[2] = "I";
                     success = PyArg_Parse (py_return, format, (unsigned int *) ret_value);
                     break;
                 }
-                case eLongInt:
+                case eScriptReturnTypeLongInt:
                 {
                     const char format[2] = "l";
                     success = PyArg_Parse (py_return, format, (long *) ret_value);
                     break;
                 }
-                case eLongIntUnsigned:
+                case eScriptReturnTypeLongIntUnsigned:
                 {
                     const char format[2] = "k";
                     success = PyArg_Parse (py_return, format, (unsigned long *) ret_value);
                     break;
                 }
-                case eLongLong:
+                case eScriptReturnTypeLongLong:
                 {
                     const char format[2] = "L";
                     success = PyArg_Parse (py_return, format, (long long *) ret_value);
                     break;
                 }
-                case eLongLongUnsigned:
+                case eScriptReturnTypeLongLongUnsigned:
                 {
                     const char format[2] = "K";
                     success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value);
                     break;
                 }
-                case eFloat:
+                case eScriptReturnTypeFloat:
                 {
                     const char format[2] = "f";
                     success = PyArg_Parse (py_return, format, (float *) ret_value);
                     break;
                 }
-                case eDouble:
+                case eScriptReturnTypeDouble:
                 {
                     const char format[2] = "d";
                     success = PyArg_Parse (py_return, format, (double *) ret_value);
                     break;
                 }
-                case eChar:
+                case eScriptReturnTypeChar:
                 {
                     const char format[2] = "c";
                     success = PyArg_Parse (py_return, format, (char *) ret_value);
@@ -1825,6 +1830,98 @@
 }
 
 bool
+ScriptInterpreterPython::LoadScriptingModule (const char* pathname,
+                                              lldb_private::Error& error)
+{
+    if (!pathname || !pathname[0])
+    {
+        error.SetErrorString("invalid pathname");
+        return false;
+    }
+    
+    if (!g_swig_call_module_init)
+    {
+        error.SetErrorString("internal helper function missing");
+        return false;
+    }
+    
+    ScriptInterpreterPython *python_interpreter = this;
+    
+    lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().GetSP();
+    
+    FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout);
+    
+    {
+        Locker py_lock(python_interpreter, tmp_fh);
+        
+        FileSpec target_file(pathname, true);
+        
+        // TODO: would we want to reject any other value?
+        if (target_file.GetFileType() == FileSpec::eFileTypeInvalid ||
+            target_file.GetFileType() == FileSpec::eFileTypeUnknown)
+        {
+            error.SetErrorString("invalid pathname");
+            return false;
+        }
+        
+        const char* directory = target_file.GetDirectory().GetCString();
+        std::string basename(target_file.GetFilename().GetCString());
+        
+        // now make sure that Python has "directory" in the search path
+        StreamString command_stream;
+        command_stream.Printf("if not (sys.path.__contains__('%s')):\n    sys.path.append('%s');\n\n",
+                              directory,
+                              directory);
+        bool syspath_retval = python_interpreter->ExecuteMultipleLines(command_stream.GetData());
+        if (!syspath_retval)
+        {
+            error.SetErrorString("Python sys.path handling failed");
+            return false;
+        }
+        
+        // strip .py or .pyc extension
+        ConstString extension = target_file.GetFileNameExtension();
+        if (::strcmp(extension.GetCString(), "py") == 0)
+            basename.resize(basename.length()-3);
+        else if(::strcmp(extension.GetCString(), "pyc") == 0)
+            basename.resize(basename.length()-4);
+        
+        // check if the module is already import-ed
+        command_stream.Clear();
+        command_stream.Printf("sys.getrefcount(%s)",basename.c_str());
+        int refcount = 0;
+        // this call will fail if the module does not exist (because the parameter to it is not a string
+        // but an actual Python module object, which is non-existant if the module was not imported before)
+        if (python_interpreter->ExecuteOneLineWithReturn(command_stream.GetData(),
+                                                         ScriptInterpreterPython::eScriptReturnTypeInt, &refcount) && refcount > 0)
+        {
+            error.SetErrorString("module already imported");
+            return false;
+        }
+
+        // now actually do the import
+        command_stream.Clear();
+        command_stream.Printf("import %s",basename.c_str());
+        bool import_retval = python_interpreter->ExecuteOneLine(command_stream.GetData(), NULL);
+        if (!import_retval)
+        {
+            error.SetErrorString("Python import statement failed");
+            return false;
+        }
+        
+        // call __lldb_module_init(debugger,dict)
+        if (!g_swig_call_module_init (basename,
+                                        python_interpreter->m_dictionary_name.c_str(),
+                                        debugger_sp))
+        {
+            error.SetErrorString("calling __lldb_module_init failed");
+            return false;
+        }
+        return true;
+    }
+}
+
+bool
 ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function,
                                                const char* args,
                                                lldb_private::CommandReturnObject& cmd_retobj,
@@ -1886,7 +1983,7 @@
     char* result_ptr = NULL; // Python is going to point this to valid data if ExecuteOneLineWithReturn returns successfully
     
     if (ExecuteOneLineWithReturn (command.c_str(),
-                                 ScriptInterpreter::eCharStrOrNone,
+                                 ScriptInterpreter::eScriptReturnTypeCharStrOrNone,
                                  &result_ptr) && result_ptr)
     {
         return std::string(result_ptr);
@@ -1905,7 +2002,8 @@
                                                 SWIGPythonGetIndexOfChildWithName python_swig_get_index_child,
                                                 SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue,
                                                 SWIGPythonUpdateSynthProviderInstance python_swig_update_provider,
-                                                SWIGPythonCallCommand python_swig_call_command)
+                                                SWIGPythonCallCommand python_swig_call_command,
+                                                SWIGPythonCallModuleInit python_swig_call_mod_init)
 {
     g_swig_init_callback = python_swig_init_callback;
     g_swig_breakpoint_callback = python_swig_breakpoint_callback;
@@ -1917,6 +2015,7 @@
     g_swig_cast_to_sbvalue = python_swig_cast_to_sbvalue;
     g_swig_update_provider = python_swig_update_provider;
     g_swig_call_command = python_swig_call_command;
+    g_swig_call_module_init = python_swig_call_mod_init;
 }
 
 void