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