1 //===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H 10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H 11 12 #if LLDB_ENABLE_PYTHON 13 14 #include <optional> 15 #include <sstream> 16 #include <tuple> 17 #include <type_traits> 18 #include <utility> 19 20 #include "lldb/Host/Config.h" 21 #include "lldb/Interpreter/ScriptedInterface.h" 22 #include "lldb/Utility/DataBufferHeap.h" 23 24 #include "PythonDataObjects.h" 25 #include "SWIGPythonBridge.h" 26 #include "ScriptInterpreterPythonImpl.h" 27 28 namespace lldb_private { 29 class ScriptInterpreterPythonImpl; 30 class ScriptedPythonInterface : virtual public ScriptedInterface { 31 public: 32 ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter); 33 ~ScriptedPythonInterface() override = default; 34 35 protected: 36 template <typename T = StructuredData::ObjectSP> ExtractValueFromPythonObject(python::PythonObject & p,Status & error)37 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { 38 return p.CreateStructuredObject(); 39 } 40 41 template <typename T = StructuredData::ObjectSP, typename... Args> Dispatch(llvm::StringRef method_name,Status & error,Args &&...args)42 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) { 43 using namespace python; 44 using Locker = ScriptInterpreterPythonImpl::Locker; 45 46 std::string caller_signature = 47 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + 48 llvm::Twine(method_name) + llvm::Twine(")")) 49 .str(); 50 if (!m_object_instance_sp) 51 return ErrorWithMessage<T>(caller_signature, "Python object ill-formed", 52 error); 53 54 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, 55 Locker::FreeLock); 56 57 PythonObject implementor(PyRefType::Borrowed, 58 (PyObject *)m_object_instance_sp->GetValue()); 59 60 if (!implementor.IsAllocated()) 61 return ErrorWithMessage<T>(caller_signature, 62 "Python implementor not allocated.", error); 63 64 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 65 auto transformed_args = TransformArgs(original_args); 66 67 llvm::Expected<PythonObject> expected_return_object = 68 llvm::make_error<llvm::StringError>("Not initialized.", 69 llvm::inconvertibleErrorCode()); 70 std::apply( 71 [&implementor, &method_name, &expected_return_object](auto &&...args) { 72 llvm::consumeError(expected_return_object.takeError()); 73 expected_return_object = 74 implementor.CallMethod(method_name.data(), args...); 75 }, 76 transformed_args); 77 78 if (llvm::Error e = expected_return_object.takeError()) { 79 error.SetErrorString(llvm::toString(std::move(e)).c_str()); 80 return ErrorWithMessage<T>(caller_signature, 81 "Python method could not be called.", error); 82 } 83 84 PythonObject py_return = std::move(expected_return_object.get()); 85 86 if (!py_return.IsAllocated()) 87 return ErrorWithMessage<T>(caller_signature, "Returned object is null.", 88 error); 89 90 // Now that we called the python method with the transformed arguments, 91 // we need to interate again over both the original and transformed 92 // parameter pack, and transform back the parameter that were passed in 93 // the original parameter pack as references or pointers. 94 if (sizeof...(Args) > 0) 95 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) 96 return ErrorWithMessage<T>( 97 caller_signature, 98 "Couldn't re-assign reference and pointer arguments.", error); 99 100 return ExtractValueFromPythonObject<T>(py_return, error); 101 } 102 103 template <typename... Args> GetStatusFromMethod(llvm::StringRef method_name,Args &&...args)104 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) { 105 Status error; 106 Dispatch<Status>(method_name, error, std::forward<Args>(args)...); 107 108 return error; 109 } 110 Transform(T object)111 template <typename T> T Transform(T object) { 112 // No Transformation for generic usage 113 return {object}; 114 } 115 Transform(Status arg)116 python::PythonObject Transform(Status arg) { 117 return python::ToSWIGWrapper(arg); 118 } 119 120 template <typename T, typename U> ReverseTransform(T & original_arg,U transformed_arg,Status & error)121 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 122 // If U is not a PythonObject, don't touch it! 123 return; 124 } 125 126 template <typename T> ReverseTransform(T & original_arg,python::PythonObject transformed_arg,Status & error)127 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 128 Status &error) { 129 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 130 } 131 132 template <std::size_t... I, typename... Args> TransformTuple(const std::tuple<Args...> & args,std::index_sequence<I...>)133 auto TransformTuple(const std::tuple<Args...> &args, 134 std::index_sequence<I...>) { 135 return std::make_tuple(Transform(std::get<I>(args))...); 136 } 137 138 // This will iterate over the Dispatch parameter pack and replace in-place 139 // every `lldb_private` argument that has a SB counterpart. 140 template <typename... Args> TransformArgs(const std::tuple<Args...> & args)141 auto TransformArgs(const std::tuple<Args...> &args) { 142 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 143 } 144 145 template <typename T, typename U> TransformBack(T & original_arg,U transformed_arg,Status & error)146 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 147 ReverseTransform(original_arg, transformed_arg, error); 148 } 149 150 template <std::size_t... I, typename... Ts, typename... Us> ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args,std::index_sequence<I...>)151 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 152 std::tuple<Us...> &transformed_args, 153 std::index_sequence<I...>) { 154 Status error; 155 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 156 error), 157 ...); 158 return error.Success(); 159 } 160 161 template <typename... Ts, typename... Us> ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args)162 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 163 std::tuple<Us...> &transformed_args) { 164 if (sizeof...(Ts) != sizeof...(Us)) 165 return false; 166 167 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 168 std::make_index_sequence<sizeof...(Ts)>()); 169 } 170 171 template <typename T, typename... Args> FormatArgs(std::string & fmt,T arg,Args...args)172 void FormatArgs(std::string &fmt, T arg, Args... args) const { 173 FormatArgs(fmt, arg); 174 FormatArgs(fmt, args...); 175 } 176 FormatArgs(std::string & fmt,T arg)177 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 178 fmt += python::PythonFormat<T>::format; 179 } 180 FormatArgs(std::string & fmt)181 void FormatArgs(std::string &fmt) const {} 182 183 // The lifetime is managed by the ScriptInterpreter 184 ScriptInterpreterPythonImpl &m_interpreter; 185 }; 186 187 template <> 188 StructuredData::ArraySP 189 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 190 python::PythonObject &p, Status &error); 191 192 template <> 193 StructuredData::DictionarySP 194 ScriptedPythonInterface::ExtractValueFromPythonObject< 195 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 196 197 template <> 198 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 199 python::PythonObject &p, Status &error); 200 201 template <> 202 lldb::DataExtractorSP 203 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 204 python::PythonObject &p, Status &error); 205 206 template <> 207 std::optional<MemoryRegionInfo> 208 ScriptedPythonInterface::ExtractValueFromPythonObject< 209 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 210 211 } // namespace lldb_private 212 213 #endif // LLDB_ENABLE_PYTHON 214 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H 215