xref: /llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (revision 6cb45aea92dc87974a0064c182600228c6e94329)
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   enum class AbstractMethodCheckerCases {
36     eNotImplemented,
37     eNotAllocated,
38     eNotCallable,
39     eValid
40   };
41 
42   llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerCases>>
43   CheckAbstractMethodImplementation(
44       const python::PythonDictionary &class_dict) const {
45 
46     using namespace python;
47 
48     std::map<llvm::StringLiteral, AbstractMethodCheckerCases> checker;
49 #define SET_ERROR_AND_CONTINUE(method_name, error)                             \
50   {                                                                            \
51     checker[method_name] = error;                                              \
52     continue;                                                                  \
53   }
54 
55     for (const llvm::StringLiteral &method_name : GetAbstractMethods()) {
56       if (!class_dict.HasKey(method_name))
57         SET_ERROR_AND_CONTINUE(method_name,
58                                AbstractMethodCheckerCases::eNotImplemented)
59       auto callable_or_err = class_dict.GetItem(method_name);
60       if (!callable_or_err)
61         SET_ERROR_AND_CONTINUE(method_name,
62                                AbstractMethodCheckerCases::eNotAllocated)
63       if (!PythonCallable::Check(callable_or_err.get().get()))
64         SET_ERROR_AND_CONTINUE(method_name,
65                                AbstractMethodCheckerCases::eNotCallable)
66       checker[method_name] = AbstractMethodCheckerCases::eValid;
67     }
68 
69 #undef HANDLE_ERROR
70 
71     return checker;
72   }
73 
74   template <typename... Args>
75   llvm::Expected<StructuredData::GenericSP>
76   CreatePluginObject(llvm::StringRef class_name,
77                      StructuredData::Generic *script_obj, Args... args) {
78     using namespace python;
79     using Locker = ScriptInterpreterPythonImpl::Locker;
80 
81     auto create_error = [](std::string message) {
82       return llvm::createStringError(llvm::inconvertibleErrorCode(), message);
83     };
84 
85     bool has_class_name = !class_name.empty();
86     bool has_interpreter_dict =
87         !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());
88     if (!has_class_name && !has_interpreter_dict && !script_obj) {
89       if (!has_class_name)
90         return create_error("Missing script class name.");
91       else if (!has_interpreter_dict)
92         return create_error("Invalid script interpreter dictionary.");
93       else
94         return create_error("Missing scripting object.");
95     }
96 
97     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
98                    Locker::FreeLock);
99 
100     PythonObject result = {};
101 
102     if (script_obj) {
103       result = PythonObject(PyRefType::Borrowed,
104                             static_cast<PyObject *>(script_obj->GetValue()));
105     } else {
106       auto dict =
107           PythonModule::MainModule().ResolveName<python::PythonDictionary>(
108               m_interpreter.GetDictionaryName());
109       if (!dict.IsAllocated())
110         return create_error(
111             llvm::formatv("Could not find interpreter dictionary: %s",
112                           m_interpreter.GetDictionaryName()));
113 
114       auto init =
115           PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
116               class_name, dict);
117       if (!init.IsAllocated())
118         return create_error(llvm::formatv("Could not find script class: {0}",
119                                           class_name.data()));
120 
121       std::tuple<Args...> original_args = std::forward_as_tuple(args...);
122       auto transformed_args = TransformArgs(original_args);
123 
124       std::string error_string;
125       llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();
126       if (!arg_info) {
127         llvm::handleAllErrors(
128             arg_info.takeError(),
129             [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
130             [&](const llvm::ErrorInfoBase &E) {
131               error_string.append(E.message());
132             });
133         return llvm::createStringError(llvm::inconvertibleErrorCode(),
134                                        error_string);
135       }
136 
137       llvm::Expected<PythonObject> expected_return_object =
138           create_error("Resulting object is not initialized.");
139 
140       std::apply(
141           [&init, &expected_return_object](auto &&...args) {
142             llvm::consumeError(expected_return_object.takeError());
143             expected_return_object = init(args...);
144           },
145           transformed_args);
146 
147       if (!expected_return_object)
148         return expected_return_object.takeError();
149       result = expected_return_object.get();
150     }
151 
152     if (!result.IsValid())
153       return create_error("Resulting object is not a valid Python Object.");
154     if (!result.HasAttribute("__class__"))
155       return create_error("Resulting object doesn't have '__class__' member.");
156 
157     PythonObject obj_class = result.GetAttributeValue("__class__");
158     if (!obj_class.IsValid())
159       return create_error("Resulting class object is not a valid.");
160     if (!obj_class.HasAttribute("__name__"))
161       return create_error(
162           "Resulting object class doesn't have '__name__' member.");
163     PythonString obj_class_name =
164         obj_class.GetAttributeValue("__name__").AsType<PythonString>();
165 
166     PythonObject object_class_mapping_proxy =
167         obj_class.GetAttributeValue("__dict__");
168     if (!obj_class.HasAttribute("__dict__"))
169       return create_error(
170           "Resulting object class doesn't have '__dict__' member.");
171 
172     PythonCallable dict_converter = PythonModule::BuiltinsModule()
173                                         .ResolveName("dict")
174                                         .AsType<PythonCallable>();
175     if (!dict_converter.IsAllocated())
176       return create_error(
177           "Python 'builtins' module doesn't have 'dict' class.");
178 
179     PythonDictionary object_class_dict =
180         dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();
181     if (!object_class_dict.IsAllocated())
182       return create_error("Coudn't create dictionary from resulting object "
183                           "class mapping proxy object.");
184 
185     auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict);
186     if (!checker_or_err)
187       return checker_or_err.takeError();
188 
189     for (const auto &method_checker : *checker_or_err)
190       switch (method_checker.second) {
191       case AbstractMethodCheckerCases::eNotImplemented:
192         LLDB_LOG(GetLog(LLDBLog::Script),
193                  "Abstract method {0}.{1} not implemented.",
194                  obj_class_name.GetString(), method_checker.first);
195         break;
196       case AbstractMethodCheckerCases::eNotAllocated:
197         LLDB_LOG(GetLog(LLDBLog::Script),
198                  "Abstract method {0}.{1} not allocated.",
199                  obj_class_name.GetString(), method_checker.first);
200         break;
201       case AbstractMethodCheckerCases::eNotCallable:
202         LLDB_LOG(GetLog(LLDBLog::Script),
203                  "Abstract method {0}.{1} not callable.",
204                  obj_class_name.GetString(), method_checker.first);
205         break;
206       case AbstractMethodCheckerCases::eValid:
207         LLDB_LOG(GetLog(LLDBLog::Script),
208                  "Abstract method {0}.{1} implemented & valid.",
209                  obj_class_name.GetString(), method_checker.first);
210         break;
211       }
212 
213     for (const auto &method_checker : *checker_or_err)
214       if (method_checker.second != AbstractMethodCheckerCases::eValid)
215         return create_error(
216             llvm::formatv("Abstract method {0}.{1} missing. Enable lldb "
217                           "script log for more details.",
218                           obj_class_name.GetString(), method_checker.first));
219 
220     m_object_instance_sp = StructuredData::GenericSP(
221         new StructuredPythonObject(std::move(result)));
222     return m_object_instance_sp;
223   }
224 
225 protected:
226   template <typename T = StructuredData::ObjectSP>
227   T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
228     return p.CreateStructuredObject();
229   }
230 
231   template <typename T = StructuredData::ObjectSP, typename... Args>
232   T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
233     using namespace python;
234     using Locker = ScriptInterpreterPythonImpl::Locker;
235 
236     std::string caller_signature =
237         llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
238                     llvm::Twine(method_name) + llvm::Twine(")"))
239             .str();
240     if (!m_object_instance_sp)
241       return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",
242                                  error);
243 
244     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
245                    Locker::FreeLock);
246 
247     PythonObject implementor(PyRefType::Borrowed,
248                              (PyObject *)m_object_instance_sp->GetValue());
249 
250     if (!implementor.IsAllocated())
251       return llvm::is_contained(GetAbstractMethods(), method_name)
252                  ? ErrorWithMessage<T>(caller_signature,
253                                        "Python implementor not allocated.",
254                                        error)
255                  : T{};
256 
257     std::tuple<Args...> original_args = std::forward_as_tuple(args...);
258     auto transformed_args = TransformArgs(original_args);
259 
260     llvm::Expected<PythonObject> expected_return_object =
261         llvm::make_error<llvm::StringError>("Not initialized.",
262                                             llvm::inconvertibleErrorCode());
263     std::apply(
264         [&implementor, &method_name, &expected_return_object](auto &&...args) {
265           llvm::consumeError(expected_return_object.takeError());
266           expected_return_object =
267               implementor.CallMethod(method_name.data(), args...);
268         },
269         transformed_args);
270 
271     if (llvm::Error e = expected_return_object.takeError()) {
272       error.SetErrorString(llvm::toString(std::move(e)).c_str());
273       return ErrorWithMessage<T>(caller_signature,
274                                  "Python method could not be called.", error);
275     }
276 
277     PythonObject py_return = std::move(expected_return_object.get());
278 
279     // Now that we called the python method with the transformed arguments,
280     // we need to interate again over both the original and transformed
281     // parameter pack, and transform back the parameter that were passed in
282     // the original parameter pack as references or pointers.
283     if (sizeof...(Args) > 0)
284       if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
285         return ErrorWithMessage<T>(
286             caller_signature,
287             "Couldn't re-assign reference and pointer arguments.", error);
288 
289     if (!py_return.IsAllocated())
290       return {};
291     return ExtractValueFromPythonObject<T>(py_return, error);
292   }
293 
294   template <typename... Args>
295   Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
296     Status error;
297     Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
298 
299     return error;
300   }
301 
302   template <typename T> T Transform(T object) {
303     // No Transformation for generic usage
304     return {object};
305   }
306 
307   python::PythonObject Transform(bool arg) {
308     // Boolean arguments need to be turned into python objects.
309     return python::PythonBoolean(arg);
310   }
311 
312   python::PythonObject Transform(Status arg) {
313     return python::SWIGBridge::ToSWIGWrapper(arg);
314   }
315 
316   python::PythonObject Transform(const StructuredDataImpl &arg) {
317     return python::SWIGBridge::ToSWIGWrapper(arg);
318   }
319 
320   python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {
321     return python::SWIGBridge::ToSWIGWrapper(arg);
322   }
323 
324   python::PythonObject Transform(lldb::ProcessSP arg) {
325     return python::SWIGBridge::ToSWIGWrapper(arg);
326   }
327 
328   python::PythonObject Transform(lldb::ThreadPlanSP arg) {
329     return python::SWIGBridge::ToSWIGWrapper(arg);
330   }
331 
332   python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
333     return python::SWIGBridge::ToSWIGWrapper(arg);
334   }
335 
336   python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
337     return python::SWIGBridge::ToSWIGWrapper(arg);
338   }
339 
340   python::PythonObject Transform(Event *arg) {
341     return python::SWIGBridge::ToSWIGWrapper(arg);
342   }
343 
344   python::PythonObject Transform(lldb::StreamSP arg) {
345     return python::SWIGBridge::ToSWIGWrapper(arg.get());
346   }
347 
348   python::PythonObject Transform(lldb::DataExtractorSP arg) {
349     return python::SWIGBridge::ToSWIGWrapper(arg);
350   }
351 
352   template <typename T, typename U>
353   void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
354     // If U is not a PythonObject, don't touch it!
355     return;
356   }
357 
358   template <typename T>
359   void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
360                         Status &error) {
361     original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
362   }
363 
364   void ReverseTransform(bool &original_arg,
365                         python::PythonObject transformed_arg, Status &error) {
366     python::PythonBoolean boolean_arg = python::PythonBoolean(
367         python::PyRefType::Borrowed, transformed_arg.get());
368     if (boolean_arg.IsValid())
369       original_arg = boolean_arg.GetValue();
370     else
371       error.SetErrorString(
372           llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION)
373               .str());
374   }
375 
376   template <std::size_t... I, typename... Args>
377   auto TransformTuple(const std::tuple<Args...> &args,
378                       std::index_sequence<I...>) {
379     return std::make_tuple(Transform(std::get<I>(args))...);
380   }
381 
382   // This will iterate over the Dispatch parameter pack and replace in-place
383   // every `lldb_private` argument that has a SB counterpart.
384   template <typename... Args>
385   auto TransformArgs(const std::tuple<Args...> &args) {
386     return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
387   }
388 
389   template <typename T, typename U>
390   void TransformBack(T &original_arg, U transformed_arg, Status &error) {
391     ReverseTransform(original_arg, transformed_arg, error);
392   }
393 
394   template <std::size_t... I, typename... Ts, typename... Us>
395   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
396                               std::tuple<Us...> &transformed_args,
397                               std::index_sequence<I...>) {
398     Status error;
399     (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
400                    error),
401      ...);
402     return error.Success();
403   }
404 
405   template <typename... Ts, typename... Us>
406   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
407                               std::tuple<Us...> &transformed_args) {
408     if (sizeof...(Ts) != sizeof...(Us))
409       return false;
410 
411     return ReassignPtrsOrRefsArgs(original_args, transformed_args,
412                                   std::make_index_sequence<sizeof...(Ts)>());
413   }
414 
415   template <typename T, typename... Args>
416   void FormatArgs(std::string &fmt, T arg, Args... args) const {
417     FormatArgs(fmt, arg);
418     FormatArgs(fmt, args...);
419   }
420 
421   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
422     fmt += python::PythonFormat<T>::format;
423   }
424 
425   void FormatArgs(std::string &fmt) const {}
426 
427   // The lifetime is managed by the ScriptInterpreter
428   ScriptInterpreterPythonImpl &m_interpreter;
429 };
430 
431 template <>
432 StructuredData::ArraySP
433 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
434     python::PythonObject &p, Status &error);
435 
436 template <>
437 StructuredData::DictionarySP
438 ScriptedPythonInterface::ExtractValueFromPythonObject<
439     StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
440 
441 template <>
442 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
443     python::PythonObject &p, Status &error);
444 
445 template <>
446 Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
447     python::PythonObject &p, Status &error);
448 
449 template <>
450 lldb::StreamSP
451 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
452     python::PythonObject &p, Status &error);
453 
454 template <>
455 lldb::BreakpointSP
456 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
457     python::PythonObject &p, Status &error);
458 
459 template <>
460 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
461     lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
462 
463 template <>
464 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
465     lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
466 
467 template <>
468 lldb::DataExtractorSP
469 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
470     python::PythonObject &p, Status &error);
471 
472 template <>
473 std::optional<MemoryRegionInfo>
474 ScriptedPythonInterface::ExtractValueFromPythonObject<
475     std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
476 
477 } // namespace lldb_private
478 
479 #endif // LLDB_ENABLE_PYTHON
480 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
481