From 6640c2c8d211f912f4059a789645019c2dd2b036 Mon Sep 17 00:00:00 2001
From: serge_sans_paille <sguelton@redhat.com>
Date: Thu, 31 Oct 2019 08:29:12 +0100
Subject: [PATCH] Revert "[Python] Remove readline module" + patches

This reverts commit 903a9c74f07c6d200decfbb2427857e5f1038bb3.
---
 lldb/scripts/CMakeLists.txt                        |  3 +
 lldb/scripts/Python/modules/CMakeLists.txt         | 11 +++
 .../Python/modules/lldb_readline/CMakeLists.txt    | 27 +++++++
 .../Python/modules/lldb_readline/lldb_readline.cpp | 87 ++++++++++++++++++++++
 lldb/source/Interpreter/embedded_interpreter.py    |  1 +
 .../Python/ScriptInterpreterPython.cpp             | 24 ++++--
 6 files changed, 148 insertions(+), 5 deletions(-)
 create mode 100644 lldb/scripts/Python/modules/CMakeLists.txt
 create mode 100644 lldb/scripts/Python/modules/lldb_readline/CMakeLists.txt
 create mode 100644 lldb/scripts/Python/modules/lldb_readline/lldb_readline.cpp

diff --git a/lldb/scripts/CMakeLists.txt b/lldb/scripts/CMakeLists.txt
index 1a0ea96..810ad4d 100644
--- a/lldb/scripts/CMakeLists.txt
+++ b/lldb/scripts/CMakeLists.txt
@@ -54,3 +54,6 @@ if(NOT LLDB_BUILD_FRAMEWORK)
   # Install the LLDB python module
   install(DIRECTORY ${SWIG_PYTHON_DIR} DESTINATION ${SWIG_INSTALL_DIR})
 endif()
+
+# build Python modules
+add_subdirectory(Python/modules)
diff --git a/lldb/scripts/Python/modules/CMakeLists.txt b/lldb/scripts/Python/modules/CMakeLists.txt
new file mode 100644
index 0000000..fc7eaa8
--- /dev/null
+++ b/lldb/scripts/Python/modules/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Disable some warnings triggered by Python's headers.
+check_cxx_compiler_flag("-Wno-macro-redefined"
+                        CXX_SUPPORTS_NO_MACRO_REDEFINED)
+if (CXX_SUPPORTS_NO_MACRO_REDEFINED)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-macro-redefined")
+endif ()
+
+# build the Python readline suppression module only on Linux
+if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "GNU" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "kFreeBSD")
+   add_subdirectory(lldb_readline)
+endif()
diff --git a/lldb/scripts/Python/modules/lldb_readline/CMakeLists.txt b/lldb/scripts/Python/modules/lldb_readline/CMakeLists.txt
new file mode 100644
index 0000000..3876698
--- /dev/null
+++ b/lldb/scripts/Python/modules/lldb_readline/CMakeLists.txt
@@ -0,0 +1,27 @@
+# FIXME: if a non-standard version of python is requested, the cmake macro
+# below will need Python_ADDITIONAL_VERSIONS set in order to find it.
+include(FindPythonInterp)
+SET(PYTHON_DIRECTORY python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
+
+# Build an alternative to readline python module
+include_directories(${PYTHON_INCLUDE_DIR})
+add_library(lldb_readline SHARED lldb_readline.cpp)
+target_link_libraries(lldb_readline ${PYTHON_LIBRARY})
+
+if (NOT LLDB_DISABLE_LIBEDIT)
+  target_include_directories(lldb_readline
+                             PRIVATE
+                               ${libedit_INCLUDE_DIRS})
+  target_link_libraries(lldb_readline ${libedit_LIBRARIES})
+endif()
+
+# FIXME: the LIBRARY_OUTPUT_PATH seems to be ignored - this is not a
+# functional issue for the build dir, though, since the shared lib dir
+# for the build is in the python shared library load path, and thus
+# python finds it when loading the python lldb_readline module.
+set_target_properties(lldb_readline PROPERTIES
+                               PREFIX ""
+                               LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY})
+
+# Install the readline module.
+install(TARGETS lldb_readline LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY})
diff --git a/lldb/scripts/Python/modules/lldb_readline/lldb_readline.cpp b/lldb/scripts/Python/modules/lldb_readline/lldb_readline.cpp
new file mode 100644
index 0000000..de729fa
--- /dev/null
+++ b/lldb/scripts/Python/modules/lldb_readline/lldb_readline.cpp
@@ -0,0 +1,87 @@
+// NOTE: Since Python may define some pre-processor definitions which affect the
+// standard headers on some systems, you must include Python.h before any
+// standard headers are included.
+#include "Python.h"
+
+#include <stdio.h>
+
+#ifndef LLDB_DISABLE_LIBEDIT
+#include <editline/readline.h>
+#endif
+
+// Simple implementation of the Python readline module using libedit.
+// In the event that libedit is excluded from the build, this turns
+// back into a null implementation that blocks the module from pulling
+// in the GNU readline shared lib, which causes linkage confusion when
+// both readline and libedit's readline compatibility symbols collide.
+//
+// Currently it only installs a PyOS_ReadlineFunctionPointer, without
+// implementing any of the readline module methods. This is meant to
+// work around LLVM pr18841 to avoid seg faults in the stock Python
+// readline.so linked against GNU readline.
+
+#ifndef LLDB_DISABLE_LIBEDIT
+PyDoc_STRVAR(moduleDocumentation,
+             "Simple readline module implementation based on libedit.");
+#else
+PyDoc_STRVAR(moduleDocumentation,
+             "Stub module meant to avoid linking GNU readline.");
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef readline_module = {
+    PyModuleDef_HEAD_INIT, // m_base
+    "lldb_editline",       // m_name
+    moduleDocumentation,   // m_doc
+    -1,                    // m_size
+    nullptr,               // m_methods
+    nullptr,               // m_reload
+    nullptr,               // m_traverse
+    nullptr,               // m_clear
+    nullptr,               // m_free
+};
+#else
+static struct PyMethodDef moduleMethods[] = {{nullptr, nullptr, 0, nullptr}};
+#endif
+
+#ifndef LLDB_DISABLE_LIBEDIT
+static char *
+#if PY_MAJOR_VERSION >= 3
+simple_readline(FILE *stdin, FILE *stdout, const char *prompt)
+#else
+simple_readline(FILE *stdin, FILE *stdout, char *prompt)
+#endif
+{
+  rl_instream = stdin;
+  rl_outstream = stdout;
+  char *line = readline(prompt);
+  if (!line) {
+    char *ret = (char *)PyMem_Malloc(1);
+    if (ret != NULL)
+      *ret = '\0';
+    return ret;
+  }
+  if (*line)
+    add_history(line);
+  int n = strlen(line);
+  char *ret = (char *)PyMem_Malloc(n + 2);
+  strncpy(ret, line, n);
+  free(line);
+  ret[n] = '\n';
+  ret[n + 1] = '\0';
+  return ret;
+}
+#endif
+
+PyMODINIT_FUNC initlldb_readline(void) {
+#ifndef LLDB_DISABLE_LIBEDIT
+  PyOS_ReadlineFunctionPointer = simple_readline;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+  return PyModule_Create(&readline_module);
+#else
+  Py_InitModule4("lldb_readline", moduleMethods, moduleDocumentation,
+                 static_cast<PyObject *>(NULL), PYTHON_API_VERSION);
+#endif
+}
diff --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py
index 8a1195d..76407c5 100644
--- a/lldb/source/Interpreter/embedded_interpreter.py
+++ b/lldb/source/Interpreter/embedded_interpreter.py
@@ -8,6 +8,7 @@ import lldb
 import traceback
 
 try:
+    sys.modules['readline'] = __import__('lldb_readline')
     import readline
     import rlcompleter
 except ImportError:
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index b376df2..689c299 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -199,10 +199,6 @@ namespace {
 // across our initialization.  We do this by using an RAII pattern where we
 // save off initial state at the beginning, and restore it at the end
 struct InitializePythonRAII {
-  static PyObject* fake_readline() {
-    PyErr_SetImportError(PyUnicode_FromString("readline"), nullptr, nullptr);
-    return nullptr;
-  }
 public:
   InitializePythonRAII()
       : m_gil_state(PyGILState_UNLOCKED), m_was_already_initialized(false) {
@@ -212,9 +208,27 @@ public:
 
     InitializePythonHome();
 
+    auto *plast = PyImport_Inittab;
+    while (plast[1].name)
+      ++plast;
+
+    for (auto *p = PyImport_Inittab; p->name != NULL; p++) {
+      if (strcmp(p->name, "readline") == 0) {
+        if (p == plast) {
+          p->name = nullptr;
+          p->initfunc = nullptr;
+        } else {
+          p->name = plast->name;
+          p->initfunc = plast->initfunc;
+          plast->name = nullptr;
+          plast->initfunc = nullptr;
+        }
+        break;
+      }
+    }
+
     // Register _lldb as a built-in module.
     PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
-    PyImport_AppendInittab("readline", &fake_readline);
 
 
 // Python < 3.2 and Python >= 3.2 reversed the ordering requirements for
-- 
1.8.3.1

