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_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_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/Interfaces/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> 37 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { 38 return p.CreateStructuredObject(); 39 } 40 41 template <typename T = StructuredData::ObjectSP, typename... 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> 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 111 template <typename T> T Transform(T object) { 112 // No Transformation for generic usage 113 return {object}; 114 } 115 116 python::PythonObject Transform(bool arg) { 117 // Boolean arguments need to be turned into python objects. 118 return python::PythonBoolean(arg); 119 } 120 121 python::PythonObject Transform(Status arg) { 122 return python::SWIGBridge::ToSWIGWrapper(arg); 123 } 124 125 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { 126 return python::SWIGBridge::ToSWIGWrapper(arg); 127 } 128 129 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { 130 return python::SWIGBridge::ToSWIGWrapper(arg); 131 } 132 133 python::PythonObject Transform(lldb::DataExtractorSP arg) { 134 return python::SWIGBridge::ToSWIGWrapper(arg); 135 } 136 137 template <typename T, typename U> 138 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 139 // If U is not a PythonObject, don't touch it! 140 return; 141 } 142 143 template <typename T> 144 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 145 Status &error) { 146 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 147 } 148 149 void ReverseTransform(bool &original_arg, 150 python::PythonObject transformed_arg, Status &error) { 151 python::PythonBoolean boolean_arg = python::PythonBoolean( 152 python::PyRefType::Borrowed, transformed_arg.get()); 153 if (boolean_arg.IsValid()) 154 original_arg = boolean_arg.GetValue(); 155 else 156 error.SetErrorString( 157 llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION) 158 .str()); 159 } 160 161 template <std::size_t... I, typename... Args> 162 auto TransformTuple(const std::tuple<Args...> &args, 163 std::index_sequence<I...>) { 164 return std::make_tuple(Transform(std::get<I>(args))...); 165 } 166 167 // This will iterate over the Dispatch parameter pack and replace in-place 168 // every `lldb_private` argument that has a SB counterpart. 169 template <typename... Args> 170 auto TransformArgs(const std::tuple<Args...> &args) { 171 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 172 } 173 174 template <typename T, typename U> 175 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 176 ReverseTransform(original_arg, transformed_arg, error); 177 } 178 179 template <std::size_t... I, typename... Ts, typename... Us> 180 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 181 std::tuple<Us...> &transformed_args, 182 std::index_sequence<I...>) { 183 Status error; 184 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 185 error), 186 ...); 187 return error.Success(); 188 } 189 190 template <typename... Ts, typename... Us> 191 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 192 std::tuple<Us...> &transformed_args) { 193 if (sizeof...(Ts) != sizeof...(Us)) 194 return false; 195 196 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 197 std::make_index_sequence<sizeof...(Ts)>()); 198 } 199 200 template <typename T, typename... Args> 201 void FormatArgs(std::string &fmt, T arg, Args... args) const { 202 FormatArgs(fmt, arg); 203 FormatArgs(fmt, args...); 204 } 205 206 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 207 fmt += python::PythonFormat<T>::format; 208 } 209 210 void FormatArgs(std::string &fmt) const {} 211 212 // The lifetime is managed by the ScriptInterpreter 213 ScriptInterpreterPythonImpl &m_interpreter; 214 }; 215 216 template <> 217 StructuredData::ArraySP 218 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 219 python::PythonObject &p, Status &error); 220 221 template <> 222 StructuredData::DictionarySP 223 ScriptedPythonInterface::ExtractValueFromPythonObject< 224 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 225 226 template <> 227 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 228 python::PythonObject &p, Status &error); 229 230 template <> 231 lldb::BreakpointSP 232 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( 233 python::PythonObject &p, Status &error); 234 235 template <> 236 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 237 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); 238 239 template <> 240 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 241 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error); 242 243 template <> 244 lldb::DataExtractorSP 245 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 246 python::PythonObject &p, Status &error); 247 248 template <> 249 std::optional<MemoryRegionInfo> 250 ScriptedPythonInterface::ExtractValueFromPythonObject< 251 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 252 253 } // namespace lldb_private 254 255 #endif // LLDB_ENABLE_PYTHON 256 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 257