JDevlieghere updated this revision to Diff 298032.
JDevlieghere added a comment.

Fix edge cases and add more tests


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89334/new/

https://reviews.llvm.org/D89334

Files:
  lldb/include/lldb/Interpreter/CommandInterpreter.h
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
  lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
  lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
  lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
  lldb/lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
  lldb/source/Commands/CommandObjectCommands.cpp
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
  lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
  lldb/test/Shell/ScriptInterpreter/Python/relative_import.test

Index: lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
@@ -0,0 +1,11 @@
+# REQUIRES: python
+
+# RUN: rm -rf %t && mkdir -p %t/foo/bar/baz
+# RUN: cp %S/Inputs/zip.py %t/foo
+# RUN: cp %S/Inputs/zip.in %t/foo
+# RUN: cp %S/Inputs/hello.py %t/foo/bar/baz
+# RUN: cp %S/Inputs/hello.in %t/foo/bar
+# RUN: echo 'command source %t/foo/bar/hello.in' >> %t/foo/zip.in
+# RUN: %lldb --script-language python -o 'command source %t/foo/zip.in' -o 'zip' -o 'hello' 2>&1 | FileCheck %s
+# CHECK: 95126
+# CHECK: Hello, World!
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
@@ -0,0 +1,7 @@
+import lldb
+
+def zip(debugger, command, result, internal_dict):
+    print("95126")
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f zip.zip zip')
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
@@ -0,0 +1 @@
+command script import zip
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
@@ -0,0 +1,7 @@
+import lldb
+
+def hello(debugger, command, result, internal_dict):
+    print("Hello, World!")
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f hello.hello hello')
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
@@ -0,0 +1 @@
+command script import baz/hello.py
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -231,10 +231,10 @@
   bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value,
                               std::string &output, Status &error) override;
 
-  bool
-  LoadScriptingModule(const char *filename, bool init_session,
-                      lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr) override;
+  bool LoadScriptingModule(const char *filename, bool init_session,
+                           lldb_private::Status &error,
+                           StructuredData::ObjectSP *module_sp = nullptr,
+                           FileSpec extra_search_dir = {}) override;
 
   bool IsReservedWord(const char *word) override;
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2733,8 +2733,9 @@
 
 bool ScriptInterpreterPythonImpl::LoadScriptingModule(
     const char *pathname, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+    StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) {
   namespace fs = llvm::sys::fs;
+  namespace path = llvm::sys::path;
 
   if (!pathname || !pathname[0]) {
     error.SetErrorString("invalid pathname");
@@ -2748,7 +2749,8 @@
   FileSystem::Instance().Collect(target_file);
   std::string basename(target_file.GetFilename().GetCString());
 
-  StreamString command_stream;
+  fs::file_status st;
+  std::error_code ec = status(target_file.GetPath(), st);
 
   // Before executing Python code, lock the GIL.
   Locker py_lock(this,
@@ -2756,31 +2758,17 @@
                      (init_session ? Locker::InitSession : 0) | Locker::NoSTDIN,
                  Locker::FreeAcquiredLock |
                      (init_session ? Locker::TearDownSession : 0));
-  fs::file_status st;
-  std::error_code ec = status(target_file.GetPath(), st);
 
-  if (ec || st.type() == fs::file_type::status_error ||
-      st.type() == fs::file_type::type_unknown ||
-      st.type() == fs::file_type::file_not_found) {
-    // if not a valid file of any sort, check if it might be a filename still
-    // dot can't be used but / and \ can, and if either is found, reject
-    if (strchr(pathname, '\\') || strchr(pathname, '/')) {
-      error.SetErrorString("invalid pathname");
-      return false;
-    }
-    basename = pathname; // not a filename, probably a package of some sort,
-                         // let it go through
-  } else if (is_directory(st) || is_regular_file(st)) {
-    if (target_file.GetDirectory().IsEmpty()) {
-      error.SetErrorString("invalid directory name");
-      return false;
+  auto ExtendSysPath = [this](std::string directory) -> llvm::Error {
+    if (directory.empty()) {
+      return llvm::make_error<llvm::StringError>(
+          "invalid directory name", llvm::inconvertibleErrorCode());
     }
 
-    std::string directory = target_file.GetDirectory().GetCString();
     replace_all(directory, "\\", "\\\\");
     replace_all(directory, "'", "\\'");
 
-    // now make sure that Python has "directory" in the search path
+    // 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.insert(1,'%s');\n\n",
@@ -2792,13 +2780,51 @@
                                  .SetSetLLDBGlobals(false))
             .Success();
     if (!syspath_retval) {
-      error.SetErrorString("Python sys.path handling failed");
-      return false;
+      return llvm::make_error<llvm::StringError>(
+          "Python sys.path handling failed", llvm::inconvertibleErrorCode());
     }
 
-  } else {
-    error.SetErrorString("no known way to import this module specification");
-    return false;
+    return llvm::Error::success();
+  };
+
+  if (ec || st.type() == fs::file_type::status_error ||
+      st.type() == fs::file_type::type_unknown ||
+      st.type() == fs::file_type::file_not_found) {
+    // The path name doesn't exists. It might be a relative import in which
+    // case we need to resolve it using to the extra search directory if
+    // provided. Python doesn't allow having directories in a module name. If
+    // the path name contains a path separator we need to add it to the system
+    // path. For example, if the pathname is `bar/baz` and the search path is
+    // `foo`, we need to add `foo/bar` to the system path and import `baz`.
+    //
+    // If no extra search path is given and the path name contains a path
+    // separator, we know it's invalid and can reject is immediately. If no
+    // path separators are found defer, to Python and let it deal with it.
+    if (extra_search_dir) {
+      FileSpec sys_path = extra_search_dir;
+      ;
+      if (ConstString directory = target_file.GetDirectory()) {
+        llvm::SmallString<128> buffer;
+        extra_search_dir.GetPath(buffer);
+        path::append(buffer, directory.GetCString());
+        sys_path.SetPath(buffer);
+      }
+      if (llvm::Error e = ExtendSysPath(sys_path.GetPath())) {
+        error = std::move(e);
+        return false;
+      }
+    } else if (strchr(pathname, '\\') || strchr(pathname, '/')) {
+      error.SetErrorString("invalid pathname");
+      return false;
+    } else {
+      basename = pathname;
+    }
+  } else if (is_directory(st) || is_regular_file(st)) {
+    if (llvm::Error e =
+            ExtendSysPath(target_file.GetDirectory().GetCString())) {
+      error = std::move(e);
+      return false;
+    }
   }
 
   // Strip .py or .pyc extension
@@ -2811,6 +2837,7 @@
   }
 
   // check if the module is already import-ed
+  StreamString command_stream;
   command_stream.Clear();
   command_stream.Printf("sys.modules.__contains__('%s')", basename.c_str());
   bool does_contain = false;
Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
+++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
@@ -25,10 +25,10 @@
 
   void ExecuteInterpreterLoop() override;
 
-  bool
-  LoadScriptingModule(const char *filename, bool init_session,
-                      lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr) override;
+  bool LoadScriptingModule(const char *filename, bool init_session,
+                           lldb_private::Status &error,
+                           StructuredData::ObjectSP *module_sp = nullptr,
+                           FileSpec extra_search_dir = {}) override;
 
   // Static Functions
   static void Initialize();
Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -124,7 +124,7 @@
 
 bool ScriptInterpreterLua::LoadScriptingModule(
     const char *filename, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+    StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) {
 
   FileSystem::Instance().Collect(filename);
   if (llvm::Error e = m_lua->LoadModule(filename)) {
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -47,9 +47,11 @@
       "This script interpreter does not support watchpoint callbacks.");
 }
 
-bool ScriptInterpreter::LoadScriptingModule(
-    const char *filename, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+bool ScriptInterpreter::LoadScriptingModule(const char *filename,
+                                            bool init_session,
+                                            lldb_private::Status &error,
+                                            StructuredData::ObjectSP *module_sp,
+                                            FileSpec extra_search_dir) {
   error.SetErrorString(
       "This script interpreter does not support importing modules.");
   return false;
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2554,11 +2554,15 @@
     debugger.SetAsyncExecution(false);
 
   m_command_source_depth++;
+  m_command_source_dirs.push_back(cmd_file.CopyByRemovingLastPathComponent());
 
   debugger.RunIOHandlerSync(io_handler_sp);
   if (!m_command_source_flags.empty())
     m_command_source_flags.pop_back();
+
+  m_command_source_dirs.pop_back();
   m_command_source_depth--;
+
   result.SetStatus(eReturnStatusSuccessFinishNoResult);
   debugger.SetAsyncExecution(old_async_execution);
 }
@@ -2964,6 +2968,12 @@
   return true;
 }
 
+FileSpec CommandInterpreter::GetCurrentSourceDir() {
+  if (m_command_source_dirs.empty())
+    return {};
+  return m_command_source_dirs.back();
+}
+
 void CommandInterpreter::GetLLDBCommandsFromIOHandler(
     const char *prompt, IOHandlerDelegate &delegate, void *baton) {
   Debugger &debugger = GetDebugger();
Index: lldb/source/Commands/CommandObjectCommands.cpp
===================================================================
--- lldb/source/Commands/CommandObjectCommands.cpp
+++ lldb/source/Commands/CommandObjectCommands.cpp
@@ -1294,6 +1294,9 @@
       return false;
     }
 
+    FileSpec source_dir =
+        GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();
+
     for (auto &entry : command.entries()) {
       Status error;
 
@@ -1308,7 +1311,7 @@
       // more)
       m_exe_ctx.Clear();
       if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule(
-              entry.c_str(), init_session, error)) {
+              entry.c_str(), init_session, error, nullptr, source_dir)) {
         result.SetStatus(eReturnStatusSuccessFinishNoResult);
       } else {
         result.AppendErrorWithFormat("module importing failed: %s",
Index: lldb/lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
===================================================================
--- /dev/null
+++ lldb/lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
@@ -0,0 +1,11 @@
+# REQUIRES: python
+
+# RUN: rm -rf %t && mkdir -p %t/foo/bar
+# RUN: cp %S/Inputs/zip.py %t/foo
+# RUN: cp %S/Inputs/zip.in %t/foo
+# RUN: cp %S/Inputs/hello.py %t/foo/bar
+# RUN: cp %S/Inputs/hello.in %t/foo/bar
+# RUN: echo 'command source %t/foo/bar/hello.in' >> %t/foo/zip.in
+# RUN: %lldb --script-language python -o 'command source %t/foo/zip.in' -o 'zip' -o 'hello' 2>&1 | FileCheck %s
+# CHECK: 95126
+# CHECK: Hello, World!
Index: lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
===================================================================
--- /dev/null
+++ lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
@@ -0,0 +1,7 @@
+import lldb
+
+def zip(debugger, command, result, internal_dict):
+    print("95126")
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f zip.zip zip')
Index: lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
===================================================================
--- /dev/null
+++ lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
@@ -0,0 +1 @@
+command script import zip
Index: lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
===================================================================
--- /dev/null
+++ lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.py
@@ -0,0 +1,7 @@
+import lldb
+
+def hello(debugger, command, result, internal_dict):
+    print("Hello, World!")
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f hello.hello hello')
Index: lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
===================================================================
--- /dev/null
+++ lldb/lldb/test/Shell/ScriptInterpreter/Python/Inputs/hello.in
@@ -0,0 +1 @@
+command script import hello
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -507,7 +507,8 @@
   virtual bool
   LoadScriptingModule(const char *filename, bool init_session,
                       lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr);
+                      StructuredData::ObjectSP *module_sp = nullptr,
+                      FileSpec extra_search_dir = {});
 
   virtual bool IsReservedWord(const char *word) { return false; }
 
Index: lldb/include/lldb/Interpreter/CommandInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -551,6 +551,8 @@
   bool SaveTranscript(CommandReturnObject &result,
                       llvm::Optional<std::string> output_file = llvm::None);
 
+  FileSpec GetCurrentSourceDir();
+
 protected:
   friend class Debugger;
 
@@ -637,7 +639,13 @@
   ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated
                                                        // children and whether
                                                        // the user has been told
+
+  // FIXME: Stop using this to control adding to the history and then replace
+  // this with m_command_source_dirs.size().
   uint32_t m_command_source_depth;
+  /// A stack of directory paths. When not empty, the last one is the directory
+  /// of the file that's currently sourced.
+  std::vector<FileSpec> m_command_source_dirs;
   std::vector<uint32_t> m_command_source_flags;
   CommandInterpreterRunResult m_result;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to