[lldb/Interpreter] Add ScriptInterpreter Wrapper for ScriptedProcess

This patch adds a ScriptedProcess interface to the ScriptInterpreter and
more specifically, to the ScriptInterpreterPython.

This interface will be used in the C++ `ScriptProcess` Process Plugin to
call the script methods.

At the moment, not all methods are implemented, they will upstreamed in
upcoming patches.

This patch also adds helper methods to the ScriptInterpreter to
convert `SBAPI` Types (SBData & SBError) to `lldb_private` types
(DataExtractor & Status).

rdar://65508855

Differential Revision: https://reviews.llvm.org/D95711

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
GitOrigin-RevId: 1f6a57c1a0fad922e04a2b1f414b092d4b0cd8b0
diff --git a/bindings/python/python-wrapper.swig b/bindings/python/python-wrapper.swig
index b189bfd..4c39e9c 100644
--- a/bindings/python/python-wrapper.swig
+++ b/bindings/python/python-wrapper.swig
@@ -276,6 +276,72 @@
 }
 
 SWIGEXPORT void*
+LLDBSwigPythonCreateScriptedProcess
+(
+    const char *python_class_name,
+    const char *session_dictionary_name,
+    const lldb::TargetSP& target_sp,
+    lldb_private::StructuredDataImpl *args_impl,
+    std::string &error_string
+)
+{
+    if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name)
+        Py_RETURN_NONE;
+
+
+    PyErr_Cleaner py_err_cleaner(true);
+
+    auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name);
+    auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict);
+
+    if (!pfunc.IsAllocated()) {
+        error_string.append("could not find script class: ");
+        error_string.append(python_class_name);
+        return nullptr;
+    }
+
+    // I do not want the SBTarget to be deallocated when going out of scope
+    // because python has ownership of it and will manage memory for this
+    // object by itself
+    PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBTarget(target_sp)));
+
+    if (!target_arg.IsAllocated())
+        Py_RETURN_NONE;
+
+    llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
+    if (!arg_info) {
+        llvm::handleAllErrors(
+            arg_info.takeError(),
+            [&](PythonException &E) {
+                error_string.append(E.ReadBacktrace());
+            },
+            [&](const llvm::ErrorInfoBase &E) {
+                error_string.append(E.message());
+            });
+        Py_RETURN_NONE;
+    }
+
+    PythonObject result = {};
+    if (arg_info.get().max_positional_args == 2) {
+        if (args_impl != nullptr) {
+           error_string.assign("args passed, but __init__ does not take an args dictionary");
+           Py_RETURN_NONE;
+        }
+        result = pfunc(target_arg, dict);
+    } else if (arg_info.get().max_positional_args >= 3) {
+        PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBStructuredData(args_impl)));
+        result = pfunc(target_arg, args_arg, dict);
+    } else {
+        error_string.assign("wrong number of arguments in __init__, should be 2 or 3 (not including self)");
+        Py_RETURN_NONE;
+    }
+
+    if (result.IsAllocated())
+        return result.release();
+    Py_RETURN_NONE;
+}
+
+SWIGEXPORT void*
 LLDBSwigPythonCreateScriptedThreadPlan
 (
     const char *python_class_name,
@@ -803,6 +869,40 @@
 }
 
 SWIGEXPORT void*
+LLDBSWIGPython_CastPyObjectToSBData
+(
+    PyObject* data
+)
+{
+    lldb::SBData* sb_ptr = nullptr;
+
+    int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBData, 0);
+
+    if (valid_cast == -1)
+        return NULL;
+
+    return sb_ptr;
+}
+
+
+SWIGEXPORT void*
+LLDBSWIGPython_CastPyObjectToSBError
+(
+    PyObject* data
+)
+{
+    lldb::SBError* sb_ptr = nullptr;
+
+    int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBError, 0);
+
+    if (valid_cast == -1)
+        return NULL;
+
+    return sb_ptr;
+}
+
+
+SWIGEXPORT void*
 LLDBSWIGPython_CastPyObjectToSBValue
 (
     PyObject* data
diff --git a/include/lldb/API/SBData.h b/include/lldb/API/SBData.h
index 95c8086..85c8110 100644
--- a/include/lldb/API/SBData.h
+++ b/include/lldb/API/SBData.h
@@ -11,6 +11,10 @@
 
 #include "lldb/API/SBDefines.h"
 
+namespace lldb_private {
+class ScriptInterpreter;
+} // namespace lldb_private
+
 namespace lldb {
 
 class LLDB_API SBData {
@@ -147,6 +151,8 @@
   friend class SBTarget;
   friend class SBValue;
 
+  friend class lldb_private::ScriptInterpreter;
+
   lldb::DataExtractorSP m_opaque_sp;
 };
 
diff --git a/include/lldb/API/SBError.h b/include/lldb/API/SBError.h
index e1960ef..f8289e2 100644
--- a/include/lldb/API/SBError.h
+++ b/include/lldb/API/SBError.h
@@ -11,6 +11,10 @@
 
 #include "lldb/API/SBDefines.h"
 
+namespace lldb_private {
+class ScriptInterpreter;
+} // namespace lldb_private
+
 namespace lldb {
 
 class LLDB_API SBError {
@@ -72,6 +76,8 @@
   friend class SBWatchpoint;
   friend class SBFile;
 
+  friend class lldb_private::ScriptInterpreter;
+
   lldb_private::Status *get();
 
   lldb_private::Status *operator->();
diff --git a/include/lldb/API/SBThreadPlan.h b/include/lldb/API/SBThreadPlan.h
index 0dc4843..269cbc6 100644
--- a/include/lldb/API/SBThreadPlan.h
+++ b/include/lldb/API/SBThreadPlan.h
@@ -17,8 +17,6 @@
 
 class LLDB_API SBThreadPlan {
 
-  friend class lldb_private::ThreadPlan;
-
 public:
   SBThreadPlan();
 
diff --git a/include/lldb/Interpreter/ScriptInterpreter.h b/include/lldb/Interpreter/ScriptInterpreter.h
index 275d61a..fa713e6 100644
--- a/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/include/lldb/Interpreter/ScriptInterpreter.h
@@ -9,12 +9,15 @@
 #ifndef LLDB_INTERPRETER_SCRIPTINTERPRETER_H
 #define LLDB_INTERPRETER_SCRIPTINTERPRETER_H
 
+#include "lldb/API/SBData.h"
+#include "lldb/API/SBError.h"
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/Communication.h"
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/Core/SearchFilter.h"
 #include "lldb/Core/StreamFile.h"
 #include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Interpreter/ScriptedProcessInterface.h"
 #include "lldb/Utility/Broadcaster.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StructuredData.h"
@@ -83,7 +86,9 @@
     eScriptReturnTypeOpaqueObject
   };
 
-  ScriptInterpreter(Debugger &debugger, lldb::ScriptLanguage script_lang);
+  ScriptInterpreter(
+      Debugger &debugger, lldb::ScriptLanguage script_lang,
+      lldb::ScriptedProcessInterfaceUP scripted_process_interface_up = {});
 
   ~ScriptInterpreter() override = default;
 
@@ -528,9 +533,19 @@
 
   lldb::ScriptLanguage GetLanguage() { return m_script_lang; }
 
+  ScriptedProcessInterface &GetScriptedProcessInterface() {
+    return *m_scripted_process_interface_up;
+  }
+
+  lldb::DataExtractorSP
+  GetDataExtractorFromSBData(const lldb::SBData &data) const;
+
+  Status GetStatusFromSBError(const lldb::SBError &error) const;
+
 protected:
   Debugger &m_debugger;
   lldb::ScriptLanguage m_script_lang;
+  lldb::ScriptedProcessInterfaceUP m_scripted_process_interface_up;
 };
 
 } // namespace lldb_private
diff --git a/include/lldb/Interpreter/ScriptedProcessInterface.h b/include/lldb/Interpreter/ScriptedProcessInterface.h
new file mode 100644
index 0000000..67fa8e3
--- /dev/null
+++ b/include/lldb/Interpreter/ScriptedProcessInterface.h
@@ -0,0 +1,64 @@
+//===-- ScriptedProcessInterface.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_SCRIPTEDPROCESSINTERFACE_H
+#define LLDB_INTERPRETER_SCRIPTEDPROCESSINTERFACE_H
+
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/lldb-private.h"
+
+#include <string>
+
+namespace lldb_private {
+class ScriptedProcessInterface {
+public:
+  ScriptedProcessInterface() : m_object_instance_sp(nullptr) {}
+
+  virtual ~ScriptedProcessInterface() = default;
+
+  virtual StructuredData::GenericSP
+  CreatePluginObject(const llvm::StringRef class_name, lldb::TargetSP target_sp,
+                     StructuredData::DictionarySP args_sp) {
+    return nullptr;
+  }
+
+  virtual Status Launch() { return Status("ScriptedProcess did not launch"); }
+
+  virtual Status Resume() { return Status("ScriptedProcess did not resume"); }
+
+  virtual lldb::MemoryRegionInfoSP
+  GetMemoryRegionContainingAddress(lldb::addr_t address) {
+    return nullptr;
+  }
+
+  virtual StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) {
+    return nullptr;
+  }
+
+  virtual StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) {
+    return nullptr;
+  }
+
+  virtual lldb::DataExtractorSP
+  ReadMemoryAtAddress(lldb::addr_t address, size_t size, Status &error) {
+    return nullptr;
+  }
+
+  virtual StructuredData::DictionarySP GetLoadedImages() { return nullptr; }
+
+  virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; }
+
+  virtual bool IsAlive() { return true; }
+
+private:
+  StructuredData::ObjectSP m_object_instance_sp;
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_SCRIPTEDPROCESSINTERFACE_H
diff --git a/include/lldb/lldb-forward.h b/include/lldb/lldb-forward.h
index a297a92..3cf5754 100644
--- a/include/lldb/lldb-forward.h
+++ b/include/lldb/lldb-forward.h
@@ -174,6 +174,7 @@
 class Scalar;
 class ScriptInterpreter;
 class ScriptInterpreterLocker;
+class ScriptedProcessInterface;
 class ScriptedSyntheticChildren;
 class SearchFilter;
 class Section;
@@ -341,6 +342,7 @@
 typedef std::weak_ptr<lldb_private::Listener> ListenerWP;
 typedef std::shared_ptr<lldb_private::MemoryHistory> MemoryHistorySP;
 typedef std::unique_ptr<lldb_private::MemoryRegionInfo> MemoryRegionInfoUP;
+typedef std::shared_ptr<lldb_private::MemoryRegionInfo> MemoryRegionInfoSP;
 typedef std::shared_ptr<lldb_private::Module> ModuleSP;
 typedef std::weak_ptr<lldb_private::Module> ModuleWP;
 typedef std::shared_ptr<lldb_private::ObjectFile> ObjectFileSP;
@@ -391,6 +393,8 @@
     ScriptSummaryFormatSP;
 typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
 typedef std::unique_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterUP;
+typedef std::unique_ptr<lldb_private::ScriptedProcessInterface>
+    ScriptedProcessInterfaceUP;
 typedef std::shared_ptr<lldb_private::Section> SectionSP;
 typedef std::unique_ptr<lldb_private::SectionList> SectionListUP;
 typedef std::weak_ptr<lldb_private::Section> SectionWP;
diff --git a/source/Interpreter/ScriptInterpreter.cpp b/source/Interpreter/ScriptInterpreter.cpp
index 2c55d08..d822ea2 100644
--- a/source/Interpreter/ScriptInterpreter.cpp
+++ b/source/Interpreter/ScriptInterpreter.cpp
@@ -26,9 +26,12 @@
 using namespace lldb;
 using namespace lldb_private;
 
-ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
-                                     lldb::ScriptLanguage script_lang)
-    : m_debugger(debugger), m_script_lang(script_lang) {}
+ScriptInterpreter::ScriptInterpreter(
+    Debugger &debugger, lldb::ScriptLanguage script_lang,
+    lldb::ScriptedProcessInterfaceUP scripted_process_interface_up)
+    : m_debugger(debugger), m_script_lang(script_lang),
+      m_scripted_process_interface_up(
+          std::move(scripted_process_interface_up)) {}
 
 void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
     std::vector<BreakpointOptions *> &bp_options_vec,
@@ -69,6 +72,19 @@
   llvm_unreachable("Unhandled ScriptInterpreter!");
 }
 
+lldb::DataExtractorSP
+ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
+  return data.m_opaque_sp;
+}
+
+Status
+ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
+  if (error.m_opaque_up)
+    return *error.m_opaque_up.get();
+
+  return Status();
+}
+
 lldb::ScriptLanguage
 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
   if (language.equals_lower(LanguageToString(eScriptLanguageNone)))
diff --git a/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
index 2cbf8bc..84115aa 100644
--- a/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
+++ b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
@@ -11,6 +11,8 @@
   PythonDataObjects.cpp
   PythonReadline.cpp
   ScriptInterpreterPython.cpp
+  ScriptedProcessPythonInterface.cpp
+  SWIGPythonBridge.cpp
 
   LINK_LIBS
     lldbBreakpoint
diff --git a/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp b/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp
new file mode 100644
index 0000000..7c7c5d7
--- /dev/null
+++ b/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp
@@ -0,0 +1,48 @@
+//===-- SWIGPythonBridge.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "SWIGPythonBridge.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+
+template <typename T> const char *GetPythonValueFormatString(T t);
+template <> const char *GetPythonValueFormatString(char *) { return "s"; }
+template <> const char *GetPythonValueFormatString(char) { return "b"; }
+template <> const char *GetPythonValueFormatString(unsigned char) {
+  return "B";
+}
+template <> const char *GetPythonValueFormatString(short) { return "h"; }
+template <> const char *GetPythonValueFormatString(unsigned short) {
+  return "H";
+}
+template <> const char *GetPythonValueFormatString(int) { return "i"; }
+template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; }
+template <> const char *GetPythonValueFormatString(long) { return "l"; }
+template <> const char *GetPythonValueFormatString(unsigned long) {
+  return "k";
+}
+template <> const char *GetPythonValueFormatString(long long) { return "L"; }
+template <> const char *GetPythonValueFormatString(unsigned long long) {
+  return "K";
+}
+template <> const char *GetPythonValueFormatString(float) { return "f"; }
+template <> const char *GetPythonValueFormatString(double) { return "d"; }
+
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
diff --git a/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
new file mode 100644
index 0000000..1843d92
--- /dev/null
+++ b/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -0,0 +1,56 @@
+//===-- ScriptInterpreterPython.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H
+
+#include <string>
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+// GetPythonValueFormatString provides a system independent type safe way to
+// convert a variable's type into a python value format. Python value formats
+// are defined in terms of builtin C types and could change from system to as
+// the underlying typedef for uint* types, size_t, off_t and other values
+// change.
+
+template <typename T> const char *GetPythonValueFormatString(T t);
+template <> const char *GetPythonValueFormatString(char *);
+template <> const char *GetPythonValueFormatString(char);
+template <> const char *GetPythonValueFormatString(unsigned char);
+template <> const char *GetPythonValueFormatString(short);
+template <> const char *GetPythonValueFormatString(unsigned short);
+template <> const char *GetPythonValueFormatString(int);
+template <> const char *GetPythonValueFormatString(unsigned int);
+template <> const char *GetPythonValueFormatString(long);
+template <> const char *GetPythonValueFormatString(unsigned long);
+template <> const char *GetPythonValueFormatString(long long);
+template <> const char *GetPythonValueFormatString(unsigned long long);
+template <> const char *GetPythonValueFormatString(float t);
+template <> const char *GetPythonValueFormatString(double t);
+
+extern "C" void *LLDBSwigPythonCreateScriptedProcess(
+    const char *python_class_name, const char *session_dictionary_name,
+    const lldb::TargetSP &target_sp, StructuredDataImpl *args_impl,
+    std::string &error_string);
+
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBData(void *data);
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data);
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data);
+
+}; // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H
diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index c4cc67c..5f3c0b2 100644
--- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -16,7 +16,11 @@
 
 #include "PythonDataObjects.h"
 #include "PythonReadline.h"
+#include "SWIGPythonBridge.h"
 #include "ScriptInterpreterPythonImpl.h"
+#include "ScriptedProcessPythonInterface.h"
+
+#include "lldb/API/SBError.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBValue.h"
 #include "lldb/Breakpoint/StoppointCallbackContext.h"
@@ -148,8 +152,6 @@
 extern "C" int LLDBSwigPython_GetIndexOfChildWithName(void *implementor,
                                                       const char *child_name);
 
-extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data);
-
 extern lldb::ValueObjectSP
 LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data);
 
@@ -532,6 +534,9 @@
       m_command_thread_state(nullptr) {
   InitializePrivate();
 
+  m_scripted_process_interface_up =
+      std::make_unique<ScriptedProcessPythonInterface>(*this);
+
   m_dictionary_name.append("_dict");
   StreamString run_string;
   run_string.Printf("%s = dict()", m_dictionary_name.c_str());
@@ -1704,35 +1709,6 @@
   return StructuredData::ArraySP();
 }
 
-// GetPythonValueFormatString provides a system independent type safe way to
-// convert a variable's type into a python value format. Python value formats
-// are defined in terms of builtin C types and could change from system to as
-// the underlying typedef for uint* types, size_t, off_t and other values
-// change.
-
-template <typename T> const char *GetPythonValueFormatString(T t);
-template <> const char *GetPythonValueFormatString(char *) { return "s"; }
-template <> const char *GetPythonValueFormatString(char) { return "b"; }
-template <> const char *GetPythonValueFormatString(unsigned char) {
-  return "B";
-}
-template <> const char *GetPythonValueFormatString(short) { return "h"; }
-template <> const char *GetPythonValueFormatString(unsigned short) {
-  return "H";
-}
-template <> const char *GetPythonValueFormatString(int) { return "i"; }
-template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; }
-template <> const char *GetPythonValueFormatString(long) { return "l"; }
-template <> const char *GetPythonValueFormatString(unsigned long) {
-  return "k";
-}
-template <> const char *GetPythonValueFormatString(long long) { return "L"; }
-template <> const char *GetPythonValueFormatString(unsigned long long) {
-  return "K";
-}
-template <> const char *GetPythonValueFormatString(float t) { return "f"; }
-template <> const char *GetPythonValueFormatString(double t) { return "d"; }
-
 StructuredData::StringSP
 ScriptInterpreterPythonImpl::OSPlugin_RegisterContextData(
     StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid) {
diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
index 5a75c0a..b8b9781 100644
--- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
+++ b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -13,6 +13,8 @@
 
 #if LLDB_ENABLE_PYTHON
 
+#include "ScriptedProcessPythonInterface.h"
+
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/Core/StructuredDataImpl.h"
diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 45dad42..93d5768 100644
--- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H
+
 #include "lldb/Host/Config.h"
 
 #if LLDB_ENABLE_PYTHON
@@ -483,4 +486,5 @@
 
 } // namespace lldb_private
 
-#endif
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H
diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp b/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
new file mode 100644
index 0000000..03f745e
--- /dev/null
+++ b/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
@@ -0,0 +1,287 @@
+//===-- ScriptedProcessPythonInterface.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "SWIGPythonBridge.h"
+#include "ScriptInterpreterPythonImpl.h"
+#include "ScriptedProcessPythonInterface.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
+    const llvm::StringRef class_name, lldb::TargetSP target_sp,
+    StructuredData::DictionarySP args_sp) {
+  if (class_name.empty())
+    return {};
+
+  std::string error_string;
+  StructuredDataImpl *args_impl = nullptr;
+  if (args_sp) {
+    args_impl = new StructuredDataImpl();
+    args_impl->SetObjectSP(args_sp);
+  }
+
+  void *ret_val;
+
+  {
+
+    Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+                   Locker::FreeLock);
+
+    ret_val = LLDBSwigPythonCreateScriptedProcess(
+        class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
+        args_impl, error_string);
+  }
+
+  m_object_instance_sp =
+      StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+
+  return m_object_instance_sp;
+}
+
+Status ScriptedProcessPythonInterface::Launch() {
+  return LaunchOrResume("launch");
+}
+
+Status ScriptedProcessPythonInterface::Resume() {
+  return LaunchOrResume("resume");
+}
+
+Status
+ScriptedProcessPythonInterface::LaunchOrResume(llvm::StringRef method_name) {
+  Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+                 Locker::FreeLock);
+
+  if (!m_object_instance_sp)
+    return Status("Python object ill-formed.");
+
+  if (!m_object_instance_sp)
+    return Status("Cannot convert Python object to StructuredData::Generic.");
+  PythonObject implementor(PyRefType::Borrowed,
+                           (PyObject *)m_object_instance_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+    return Status("Python implementor not allocated.");
+
+  PythonObject pmeth(
+      PyRefType::Owned,
+      PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+    return Status("Python method not allocated.");
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+    if (PyErr_Occurred())
+      PyErr_Clear();
+    return Status("Python method not callable.");
+  }
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  PythonObject py_return(PyRefType::Owned,
+                         PyObject_CallMethod(implementor.get(),
+                                             method_name.str().c_str(),
+                                             nullptr));
+
+  if (PyErr_Occurred()) {
+    PyErr_Print();
+    PyErr_Clear();
+    return Status("Python method could not be called.");
+  }
+
+  if (PyObject *py_ret_ptr = py_return.get()) {
+    lldb::SBError *sb_error =
+        (lldb::SBError *)LLDBSWIGPython_CastPyObjectToSBError(py_ret_ptr);
+
+    if (!sb_error)
+      return Status("Couldn't cast lldb::SBError to lldb::Status.");
+
+    Status status = m_interpreter.GetStatusFromSBError(*sb_error);
+
+    if (status.Fail())
+      return Status("error: %s", status.AsCString());
+
+    return status;
+  }
+
+  return Status("Returned object is null.");
+}
+
+size_t
+ScriptedProcessPythonInterface::GetGenericInteger(llvm::StringRef method_name) {
+  Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+                 Locker::FreeLock);
+
+  if (!m_object_instance_sp)
+    return LLDB_INVALID_ADDRESS;
+
+  if (!m_object_instance_sp)
+    return LLDB_INVALID_ADDRESS;
+  PythonObject implementor(PyRefType::Borrowed,
+                           (PyObject *)m_object_instance_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+    return LLDB_INVALID_ADDRESS;
+
+  PythonObject pmeth(
+      PyRefType::Owned,
+      PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+    return LLDB_INVALID_ADDRESS;
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+    if (PyErr_Occurred())
+      PyErr_Clear();
+    return LLDB_INVALID_ADDRESS;
+  }
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  PythonObject py_return(PyRefType::Owned,
+                         PyObject_CallMethod(implementor.get(),
+                                             method_name.str().c_str(),
+                                             nullptr));
+
+  if (PyErr_Occurred()) {
+    PyErr_Print();
+    PyErr_Clear();
+  }
+
+  if (py_return.get()) {
+    auto size = py_return.AsUnsignedLongLong();
+    return (size) ? *size : LLDB_INVALID_ADDRESS;
+  }
+  return LLDB_INVALID_ADDRESS;
+}
+
+lldb::MemoryRegionInfoSP
+ScriptedProcessPythonInterface::GetMemoryRegionContainingAddress(
+    lldb::addr_t address) {
+  // TODO: Implement
+  return nullptr;
+}
+
+StructuredData::DictionarySP
+ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) {
+  // TODO: Implement
+  return nullptr;
+}
+
+StructuredData::DictionarySP
+ScriptedProcessPythonInterface::GetRegistersForThread(lldb::tid_t tid) {
+  // TODO: Implement
+  return nullptr;
+}
+
+lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
+    lldb::addr_t address, size_t size, Status &error) {
+  Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+                 Locker::FreeLock);
+
+  auto error_with_message = [&error](llvm::StringRef message) {
+    error.SetErrorString(message);
+    return nullptr;
+  };
+
+  static char callee_name[] = "read_memory_at_address";
+  std::string param_format = GetPythonValueFormatString(address);
+  param_format += GetPythonValueFormatString(size);
+
+  if (!m_object_instance_sp)
+    return error_with_message("Python object ill-formed.");
+
+  if (!m_object_instance_sp)
+    return error_with_message("Python method not callable.");
+
+  PythonObject implementor(PyRefType::Borrowed,
+                           (PyObject *)m_object_instance_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+    return error_with_message("Python implementor not allocated.");
+
+  PythonObject pmeth(PyRefType::Owned,
+                     PyObject_GetAttrString(implementor.get(), callee_name));
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+    return error_with_message("Python method not allocated.");
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+    if (PyErr_Occurred())
+      PyErr_Clear();
+    return error_with_message("Python method not callable.");
+  }
+
+  if (PyErr_Occurred())
+    PyErr_Clear();
+
+  PythonObject py_return(PyRefType::Owned,
+                         PyObject_CallMethod(implementor.get(), callee_name,
+                                             param_format.c_str(), address,
+                                             size));
+
+  if (PyErr_Occurred()) {
+    PyErr_Print();
+    PyErr_Clear();
+    return error_with_message("Python method could not be called.");
+  }
+
+  if (PyObject *py_ret_ptr = py_return.get()) {
+    lldb::SBData *sb_data =
+        (lldb::SBData *)LLDBSWIGPython_CastPyObjectToSBData(py_ret_ptr);
+
+    if (!sb_data)
+      return error_with_message(
+          "Couldn't cast lldb::SBData to lldb::DataExtractor.");
+
+    return m_interpreter.GetDataExtractorFromSBData(*sb_data);
+  }
+
+  return error_with_message("Returned object is null.");
+}
+
+StructuredData::DictionarySP ScriptedProcessPythonInterface::GetLoadedImages() {
+  // TODO: Implement
+  return nullptr;
+}
+
+lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() {
+  size_t pid = GetGenericInteger("get_process_id");
+
+  return (pid >= std::numeric_limits<lldb::pid_t>::max())
+             ? LLDB_INVALID_PROCESS_ID
+             : pid;
+}
+
+bool ScriptedProcessPythonInterface::IsAlive() {
+  return GetGenericInteger("is_alive");
+  ;
+}
+
+#endif
diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h b/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
new file mode 100644
index 0000000..fc07c92
--- /dev/null
+++ b/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
@@ -0,0 +1,61 @@
+//===-- ScriptedProcessPythonInterface.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "lldb/Interpreter/ScriptedProcessInterface.h"
+
+namespace lldb_private {
+class ScriptInterpreterPythonImpl;
+class ScriptedProcessPythonInterface : public ScriptedProcessInterface {
+public:
+  ScriptedProcessPythonInterface(ScriptInterpreterPythonImpl &interpreter)
+      : ScriptedProcessInterface(), m_interpreter(interpreter) {}
+
+  StructuredData::GenericSP
+  CreatePluginObject(const llvm::StringRef class_name, lldb::TargetSP target_sp,
+                     StructuredData::DictionarySP args_sp) override;
+
+  Status Launch() override;
+
+  Status Resume() override;
+
+  lldb::MemoryRegionInfoSP
+  GetMemoryRegionContainingAddress(lldb::addr_t address) override;
+
+  StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) override;
+
+  StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) override;
+
+  lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size,
+                                            Status &error) override;
+
+  StructuredData::DictionarySP GetLoadedImages() override;
+
+  lldb::pid_t GetProcessID() override;
+
+  bool IsAlive() override;
+
+protected:
+  size_t GetGenericInteger(llvm::StringRef method_name);
+  Status LaunchOrResume(llvm::StringRef method_name);
+
+private:
+  // The lifetime is managed by the ScriptInterpreter
+  ScriptInterpreterPythonImpl &m_interpreter;
+  StructuredData::GenericSP m_object_instance_sp;
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H
diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 58ddf0c..f93733b 100644
--- a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -12,6 +12,7 @@
 
 #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
 #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h"
+#include "lldb/API/SBError.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 
@@ -153,6 +154,14 @@
   return 0;
 }
 
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBData(void *data) {
+  return nullptr;
+}
+
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data) {
+  return nullptr;
+}
+
 extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data) {
   return nullptr;
 }
@@ -207,6 +216,13 @@
   return nullptr;
 }
 
+extern "C" void *LLDBSwigPythonCreateScriptedProcess(
+    const char *python_class_name, const char *session_dictionary_name,
+    const lldb::TargetSP &target_sp, StructuredDataImpl *args_impl,
+    std::string &error_string) {
+  return nullptr;
+}
+
 extern "C" void *
 LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name,
                                      const char *session_dictionary_name) {