xref: /llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (revision 7a1e8783586ecc90ee15f12c7b76799313bb32e8)
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 std::move(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::ProcessSP arg) {
224     return python::SWIGBridge::ToSWIGWrapper(arg);
225   }
226 
227   python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
228     return python::SWIGBridge::ToSWIGWrapper(arg);
229   }
230 
231   python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
232     return python::SWIGBridge::ToSWIGWrapper(arg);
233   }
234 
235   python::PythonObject Transform(lldb::DataExtractorSP arg) {
236     return python::SWIGBridge::ToSWIGWrapper(arg);
237   }
238 
239   template <typename T, typename U>
240   void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
241     // If U is not a PythonObject, don't touch it!
242     return;
243   }
244 
245   template <typename T>
246   void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
247                         Status &error) {
248     original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
249   }
250 
251   void ReverseTransform(bool &original_arg,
252                         python::PythonObject transformed_arg, Status &error) {
253     python::PythonBoolean boolean_arg = python::PythonBoolean(
254         python::PyRefType::Borrowed, transformed_arg.get());
255     if (boolean_arg.IsValid())
256       original_arg = boolean_arg.GetValue();
257     else
258       error.SetErrorString(
259           llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION)
260               .str());
261   }
262 
263   template <std::size_t... I, typename... Args>
264   auto TransformTuple(const std::tuple<Args...> &args,
265                       std::index_sequence<I...>) {
266     return std::make_tuple(Transform(std::get<I>(args))...);
267   }
268 
269   // This will iterate over the Dispatch parameter pack and replace in-place
270   // every `lldb_private` argument that has a SB counterpart.
271   template <typename... Args>
272   auto TransformArgs(const std::tuple<Args...> &args) {
273     return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
274   }
275 
276   template <typename T, typename U>
277   void TransformBack(T &original_arg, U transformed_arg, Status &error) {
278     ReverseTransform(original_arg, transformed_arg, error);
279   }
280 
281   template <std::size_t... I, typename... Ts, typename... Us>
282   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
283                               std::tuple<Us...> &transformed_args,
284                               std::index_sequence<I...>) {
285     Status error;
286     (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
287                    error),
288      ...);
289     return error.Success();
290   }
291 
292   template <typename... Ts, typename... Us>
293   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
294                               std::tuple<Us...> &transformed_args) {
295     if (sizeof...(Ts) != sizeof...(Us))
296       return false;
297 
298     return ReassignPtrsOrRefsArgs(original_args, transformed_args,
299                                   std::make_index_sequence<sizeof...(Ts)>());
300   }
301 
302   template <typename T, typename... Args>
303   void FormatArgs(std::string &fmt, T arg, Args... args) const {
304     FormatArgs(fmt, arg);
305     FormatArgs(fmt, args...);
306   }
307 
308   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
309     fmt += python::PythonFormat<T>::format;
310   }
311 
312   void FormatArgs(std::string &fmt) const {}
313 
314   // The lifetime is managed by the ScriptInterpreter
315   ScriptInterpreterPythonImpl &m_interpreter;
316 };
317 
318 template <>
319 StructuredData::ArraySP
320 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
321     python::PythonObject &p, Status &error);
322 
323 template <>
324 StructuredData::DictionarySP
325 ScriptedPythonInterface::ExtractValueFromPythonObject<
326     StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
327 
328 template <>
329 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
330     python::PythonObject &p, Status &error);
331 
332 template <>
333 lldb::BreakpointSP
334 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
335     python::PythonObject &p, Status &error);
336 
337 template <>
338 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
339     lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
340 
341 template <>
342 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
343     lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
344 
345 template <>
346 lldb::DataExtractorSP
347 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
348     python::PythonObject &p, Status &error);
349 
350 template <>
351 std::optional<MemoryRegionInfo>
352 ScriptedPythonInterface::ExtractValueFromPythonObject<
353     std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
354 
355 } // namespace lldb_private
356 
357 #endif // LLDB_ENABLE_PYTHON
358 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
359