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