xref: /llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (revision f22d82cef28a882cec4d242910933e9f5d7dcdce)
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   template <typename... Args>
36   llvm::Expected<StructuredData::GenericSP>
37   CreatePluginObject(llvm::StringRef class_name,
38                      StructuredData::Generic *script_obj, Args... args) {
39     using namespace python;
40     using Locker = ScriptInterpreterPythonImpl::Locker;
41 
42     bool has_class_name = !class_name.empty();
43     bool has_interpreter_dict =
44         !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());
45     if (!has_class_name && !has_interpreter_dict && !script_obj) {
46       if (!has_class_name)
47         return llvm::createStringError(llvm::inconvertibleErrorCode(),
48                                        "Missing script class name.");
49       else if (!has_interpreter_dict)
50         return llvm::createStringError(
51             llvm::inconvertibleErrorCode(),
52             "Invalid script interpreter dictionary.");
53       else
54         return llvm::createStringError(llvm::inconvertibleErrorCode(),
55                                        "Missing scripting object.");
56     }
57 
58     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
59                    Locker::FreeLock);
60 
61     PythonObject result = {};
62 
63     if (script_obj) {
64       result = PythonObject(PyRefType::Borrowed,
65                             static_cast<PyObject *>(script_obj->GetValue()));
66     } else {
67       auto dict =
68           PythonModule::MainModule().ResolveName<python::PythonDictionary>(
69               m_interpreter.GetDictionaryName());
70       if (!dict.IsAllocated()) {
71         return llvm::createStringError(
72             llvm::inconvertibleErrorCode(),
73             "Could not find interpreter dictionary: %s",
74             m_interpreter.GetDictionaryName());
75       }
76 
77       auto method =
78           PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
79               class_name, dict);
80       if (!method.IsAllocated())
81         return llvm::createStringError(llvm::inconvertibleErrorCode(),
82                                        "Could not find script class: %s",
83                                        class_name.data());
84 
85       std::tuple<Args...> original_args = std::forward_as_tuple(args...);
86       auto transformed_args = TransformArgs(original_args);
87 
88       std::string error_string;
89       llvm::Expected<PythonCallable::ArgInfo> arg_info = method.GetArgInfo();
90       if (!arg_info) {
91         llvm::handleAllErrors(
92             arg_info.takeError(),
93             [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
94             [&](const llvm::ErrorInfoBase &E) {
95               error_string.append(E.message());
96             });
97         return llvm::createStringError(llvm::inconvertibleErrorCode(),
98                                        error_string);
99       }
100 
101       llvm::Expected<PythonObject> expected_return_object =
102           llvm::createStringError(llvm::inconvertibleErrorCode(),
103                                   "Resulting object is not initialized.");
104 
105       std::apply(
106           [&method, &expected_return_object](auto &&...args) {
107             llvm::consumeError(expected_return_object.takeError());
108             expected_return_object = method(args...);
109           },
110           transformed_args);
111 
112       if (llvm::Error e = expected_return_object.takeError())
113         return e;
114       result = std::move(expected_return_object.get());
115     }
116 
117     if (!result.IsValid())
118       return llvm::createStringError(
119           llvm::inconvertibleErrorCode(),
120           "Resulting object is not a valid Python Object.");
121 
122     m_object_instance_sp = StructuredData::GenericSP(
123         new StructuredPythonObject(std::move(result)));
124     return m_object_instance_sp;
125   }
126 
127 protected:
128   template <typename T = StructuredData::ObjectSP>
129   T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
130     return p.CreateStructuredObject();
131   }
132 
133   template <typename T = StructuredData::ObjectSP, typename... Args>
134   T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
135     using namespace python;
136     using Locker = ScriptInterpreterPythonImpl::Locker;
137 
138     std::string caller_signature =
139         llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
140                     llvm::Twine(method_name) + llvm::Twine(")"))
141             .str();
142     if (!m_object_instance_sp)
143       return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",
144                                  error);
145 
146     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
147                    Locker::FreeLock);
148 
149     PythonObject implementor(PyRefType::Borrowed,
150                              (PyObject *)m_object_instance_sp->GetValue());
151 
152     if (!implementor.IsAllocated())
153       return ErrorWithMessage<T>(caller_signature,
154                                  "Python implementor not allocated.", error);
155 
156     std::tuple<Args...> original_args = std::forward_as_tuple(args...);
157     auto transformed_args = TransformArgs(original_args);
158 
159     llvm::Expected<PythonObject> expected_return_object =
160         llvm::make_error<llvm::StringError>("Not initialized.",
161                                             llvm::inconvertibleErrorCode());
162     std::apply(
163         [&implementor, &method_name, &expected_return_object](auto &&...args) {
164           llvm::consumeError(expected_return_object.takeError());
165           expected_return_object =
166               implementor.CallMethod(method_name.data(), args...);
167         },
168         transformed_args);
169 
170     if (llvm::Error e = expected_return_object.takeError()) {
171       error.SetErrorString(llvm::toString(std::move(e)).c_str());
172       return ErrorWithMessage<T>(caller_signature,
173                                  "Python method could not be called.", error);
174     }
175 
176     PythonObject py_return = std::move(expected_return_object.get());
177 
178     // Now that we called the python method with the transformed arguments,
179     // we need to interate again over both the original and transformed
180     // parameter pack, and transform back the parameter that were passed in
181     // the original parameter pack as references or pointers.
182     if (sizeof...(Args) > 0)
183       if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
184         return ErrorWithMessage<T>(
185             caller_signature,
186             "Couldn't re-assign reference and pointer arguments.", error);
187 
188     if (!py_return.IsAllocated())
189       return {};
190     return ExtractValueFromPythonObject<T>(py_return, error);
191   }
192 
193   template <typename... Args>
194   Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
195     Status error;
196     Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
197 
198     return error;
199   }
200 
201   template <typename T> T Transform(T object) {
202     // No Transformation for generic usage
203     return {object};
204   }
205 
206   python::PythonObject Transform(bool arg) {
207     // Boolean arguments need to be turned into python objects.
208     return python::PythonBoolean(arg);
209   }
210 
211   python::PythonObject Transform(Status arg) {
212     return python::SWIGBridge::ToSWIGWrapper(arg);
213   }
214 
215   python::PythonObject Transform(const StructuredDataImpl &arg) {
216     return python::SWIGBridge::ToSWIGWrapper(arg);
217   }
218 
219   python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {
220     return python::SWIGBridge::ToSWIGWrapper(arg);
221   }
222 
223   python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
224     return python::SWIGBridge::ToSWIGWrapper(arg);
225   }
226 
227   python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
228     return python::SWIGBridge::ToSWIGWrapper(arg);
229   }
230 
231   python::PythonObject Transform(lldb::DataExtractorSP arg) {
232     return python::SWIGBridge::ToSWIGWrapper(arg);
233   }
234 
235   template <typename T, typename U>
236   void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
237     // If U is not a PythonObject, don't touch it!
238     return;
239   }
240 
241   template <typename T>
242   void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
243                         Status &error) {
244     original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
245   }
246 
247   void ReverseTransform(bool &original_arg,
248                         python::PythonObject transformed_arg, Status &error) {
249     python::PythonBoolean boolean_arg = python::PythonBoolean(
250         python::PyRefType::Borrowed, transformed_arg.get());
251     if (boolean_arg.IsValid())
252       original_arg = boolean_arg.GetValue();
253     else
254       error.SetErrorString(
255           llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION)
256               .str());
257   }
258 
259   template <std::size_t... I, typename... Args>
260   auto TransformTuple(const std::tuple<Args...> &args,
261                       std::index_sequence<I...>) {
262     return std::make_tuple(Transform(std::get<I>(args))...);
263   }
264 
265   // This will iterate over the Dispatch parameter pack and replace in-place
266   // every `lldb_private` argument that has a SB counterpart.
267   template <typename... Args>
268   auto TransformArgs(const std::tuple<Args...> &args) {
269     return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
270   }
271 
272   template <typename T, typename U>
273   void TransformBack(T &original_arg, U transformed_arg, Status &error) {
274     ReverseTransform(original_arg, transformed_arg, error);
275   }
276 
277   template <std::size_t... I, typename... Ts, typename... Us>
278   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
279                               std::tuple<Us...> &transformed_args,
280                               std::index_sequence<I...>) {
281     Status error;
282     (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
283                    error),
284      ...);
285     return error.Success();
286   }
287 
288   template <typename... Ts, typename... Us>
289   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
290                               std::tuple<Us...> &transformed_args) {
291     if (sizeof...(Ts) != sizeof...(Us))
292       return false;
293 
294     return ReassignPtrsOrRefsArgs(original_args, transformed_args,
295                                   std::make_index_sequence<sizeof...(Ts)>());
296   }
297 
298   template <typename T, typename... Args>
299   void FormatArgs(std::string &fmt, T arg, Args... args) const {
300     FormatArgs(fmt, arg);
301     FormatArgs(fmt, args...);
302   }
303 
304   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
305     fmt += python::PythonFormat<T>::format;
306   }
307 
308   void FormatArgs(std::string &fmt) const {}
309 
310   // The lifetime is managed by the ScriptInterpreter
311   ScriptInterpreterPythonImpl &m_interpreter;
312 };
313 
314 template <>
315 StructuredData::ArraySP
316 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
317     python::PythonObject &p, Status &error);
318 
319 template <>
320 StructuredData::DictionarySP
321 ScriptedPythonInterface::ExtractValueFromPythonObject<
322     StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
323 
324 template <>
325 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
326     python::PythonObject &p, Status &error);
327 
328 template <>
329 lldb::BreakpointSP
330 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
331     python::PythonObject &p, Status &error);
332 
333 template <>
334 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
335     lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
336 
337 template <>
338 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
339     lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
340 
341 template <>
342 lldb::DataExtractorSP
343 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
344     python::PythonObject &p, Status &error);
345 
346 template <>
347 std::optional<MemoryRegionInfo>
348 ScriptedPythonInterface::ExtractValueFromPythonObject<
349     std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
350 
351 } // namespace lldb_private
352 
353 #endif // LLDB_ENABLE_PYTHON
354 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
355