xref: /llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (revision f732157a9d067e4d300905c831a964222e0eadee)
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       // This relax the requirement on the number of argument for
184       // initializing scripting extension if the size of the interface
185       // parameter pack contains 1 less element than the extension maximum
186       // number of positional arguments for this initializer.
187       //
188       // This addresses the cases where the embedded interpreter session
189       // dictionary is passed to the extension initializer which is not used
190       // most of the time.
191       size_t num_args = sizeof...(Args);
192       if (num_args != arg_info->max_positional_args) {
193         if (num_args != arg_info->max_positional_args - 1)
194           return create_error("Passed arguments ({0}) doesn't match the number "
195                               "of expected arguments ({1}).",
196                               num_args, arg_info->max_positional_args);
197 
198         std::apply(
199             [&init, &expected_return_object](auto &&...args) {
200               llvm::consumeError(expected_return_object.takeError());
201               expected_return_object = init(args...);
202             },
203             std::tuple_cat(transformed_args, std::make_tuple(dict)));
204       } else {
205         std::apply(
206             [&init, &expected_return_object](auto &&...args) {
207               llvm::consumeError(expected_return_object.takeError());
208               expected_return_object = init(args...);
209             },
210             transformed_args);
211       }
212 
213       if (!expected_return_object)
214         return expected_return_object.takeError();
215       result = expected_return_object.get();
216     }
217 
218     if (!result.IsValid())
219       return create_error("Resulting object is not a valid Python Object.");
220     if (!result.HasAttribute("__class__"))
221       return create_error("Resulting object doesn't have '__class__' member.");
222 
223     PythonObject obj_class = result.GetAttributeValue("__class__");
224     if (!obj_class.IsValid())
225       return create_error("Resulting class object is not a valid.");
226     if (!obj_class.HasAttribute("__name__"))
227       return create_error(
228           "Resulting object class doesn't have '__name__' member.");
229     PythonString obj_class_name =
230         obj_class.GetAttributeValue("__name__").AsType<PythonString>();
231 
232     PythonObject object_class_mapping_proxy =
233         obj_class.GetAttributeValue("__dict__");
234     if (!obj_class.HasAttribute("__dict__"))
235       return create_error(
236           "Resulting object class doesn't have '__dict__' member.");
237 
238     PythonCallable dict_converter = PythonModule::BuiltinsModule()
239                                         .ResolveName("dict")
240                                         .AsType<PythonCallable>();
241     if (!dict_converter.IsAllocated())
242       return create_error(
243           "Python 'builtins' module doesn't have 'dict' class.");
244 
245     PythonDictionary object_class_dict =
246         dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();
247     if (!object_class_dict.IsAllocated())
248       return create_error("Coudn't create dictionary from resulting object "
249                           "class mapping proxy object.");
250 
251     auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict);
252     if (!checker_or_err)
253       return checker_or_err.takeError();
254 
255     llvm::Error abstract_method_errors = llvm::Error::success();
256     for (const auto &method_checker : *checker_or_err)
257       switch (method_checker.second.checker_case) {
258       case AbstractMethodCheckerCases::eNotImplemented:
259         abstract_method_errors = llvm::joinErrors(
260             std::move(abstract_method_errors),
261             std::move(create_error("Abstract method {0}.{1} not implemented.",
262                                    obj_class_name.GetString(),
263                                    method_checker.first)));
264         break;
265       case AbstractMethodCheckerCases::eNotAllocated:
266         abstract_method_errors = llvm::joinErrors(
267             std::move(abstract_method_errors),
268             std::move(create_error("Abstract method {0}.{1} not allocated.",
269                                    obj_class_name.GetString(),
270                                    method_checker.first)));
271         break;
272       case AbstractMethodCheckerCases::eNotCallable:
273         abstract_method_errors = llvm::joinErrors(
274             std::move(abstract_method_errors),
275             std::move(create_error("Abstract method {0}.{1} not callable.",
276                                    obj_class_name.GetString(),
277                                    method_checker.first)));
278         break;
279       case AbstractMethodCheckerCases::eUnknownArgumentCount:
280         abstract_method_errors = llvm::joinErrors(
281             std::move(abstract_method_errors),
282             std::move(create_error(
283                 "Abstract method {0}.{1} has unknown argument count.",
284                 obj_class_name.GetString(), method_checker.first)));
285         break;
286       case AbstractMethodCheckerCases::eInvalidArgumentCount: {
287         auto &payload_variant = method_checker.second.payload;
288         if (!std::holds_alternative<
289                 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
290                 payload_variant)) {
291           abstract_method_errors = llvm::joinErrors(
292               std::move(abstract_method_errors),
293               std::move(create_error(
294                   "Abstract method {0}.{1} has unexpected argument count.",
295                   obj_class_name.GetString(), method_checker.first)));
296         } else {
297           auto payload = std::get<
298               AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
299               payload_variant);
300           abstract_method_errors = llvm::joinErrors(
301               std::move(abstract_method_errors),
302               std::move(
303                   create_error("Abstract method {0}.{1} has unexpected "
304                                "argument count (expected {2} but has {3}).",
305                                obj_class_name.GetString(), method_checker.first,
306                                payload.required_argument_count,
307                                payload.actual_argument_count)));
308         }
309       } break;
310       case AbstractMethodCheckerCases::eValid:
311         LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",
312                  obj_class_name.GetString(), method_checker.first);
313         break;
314       }
315 
316     if (abstract_method_errors) {
317       Status error = Status::FromError(std::move(abstract_method_errors));
318       LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name,
319                error.AsCString());
320       return error.ToError();
321     }
322 
323     m_object_instance_sp = StructuredData::GenericSP(
324         new StructuredPythonObject(std::move(result)));
325     return m_object_instance_sp;
326   }
327 
328 protected:
329   template <typename T = StructuredData::ObjectSP>
330   T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
331     return p.CreateStructuredObject();
332   }
333 
334   template <typename T = StructuredData::ObjectSP, typename... Args>
335   T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
336     using namespace python;
337     using Locker = ScriptInterpreterPythonImpl::Locker;
338 
339     std::string caller_signature =
340         llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
341                     llvm::Twine(method_name) + llvm::Twine(")"))
342             .str();
343     if (!m_object_instance_sp)
344       return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",
345                                  error);
346 
347     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
348                    Locker::FreeLock);
349 
350     PythonObject implementor(PyRefType::Borrowed,
351                              (PyObject *)m_object_instance_sp->GetValue());
352 
353     if (!implementor.IsAllocated())
354       return llvm::is_contained(GetAbstractMethods(), method_name)
355                  ? ErrorWithMessage<T>(caller_signature,
356                                        "Python implementor not allocated.",
357                                        error)
358                  : T{};
359 
360     std::tuple<Args...> original_args = std::forward_as_tuple(args...);
361     auto transformed_args = TransformArgs(original_args);
362 
363     llvm::Expected<PythonObject> expected_return_object =
364         llvm::make_error<llvm::StringError>("Not initialized.",
365                                             llvm::inconvertibleErrorCode());
366     std::apply(
367         [&implementor, &method_name, &expected_return_object](auto &&...args) {
368           llvm::consumeError(expected_return_object.takeError());
369           expected_return_object =
370               implementor.CallMethod(method_name.data(), args...);
371         },
372         transformed_args);
373 
374     if (llvm::Error e = expected_return_object.takeError()) {
375       error = Status::FromError(std::move(e));
376       return ErrorWithMessage<T>(caller_signature,
377                                  "Python method could not be called.", error);
378     }
379 
380     PythonObject py_return = std::move(expected_return_object.get());
381 
382     // Now that we called the python method with the transformed arguments,
383     // we need to interate again over both the original and transformed
384     // parameter pack, and transform back the parameter that were passed in
385     // the original parameter pack as references or pointers.
386     if (sizeof...(Args) > 0)
387       if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
388         return ErrorWithMessage<T>(
389             caller_signature,
390             "Couldn't re-assign reference and pointer arguments.", error);
391 
392     if (!py_return.IsAllocated())
393       return {};
394     return ExtractValueFromPythonObject<T>(py_return, error);
395   }
396 
397   template <typename... Args>
398   Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
399     Status error;
400     Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
401 
402     return error;
403   }
404 
405   template <typename T> T Transform(T object) {
406     // No Transformation for generic usage
407     return {object};
408   }
409 
410   python::PythonObject Transform(bool arg) {
411     // Boolean arguments need to be turned into python objects.
412     return python::PythonBoolean(arg);
413   }
414 
415   python::PythonObject Transform(const Status &arg) {
416     return python::SWIGBridge::ToSWIGWrapper(arg.Clone());
417   }
418 
419   python::PythonObject Transform(Status &&arg) {
420     return python::SWIGBridge::ToSWIGWrapper(std::move(arg));
421   }
422 
423   python::PythonObject Transform(const StructuredDataImpl &arg) {
424     return python::SWIGBridge::ToSWIGWrapper(arg);
425   }
426 
427   python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {
428     return python::SWIGBridge::ToSWIGWrapper(arg);
429   }
430 
431   python::PythonObject Transform(lldb::TargetSP arg) {
432     return python::SWIGBridge::ToSWIGWrapper(arg);
433   }
434 
435   python::PythonObject Transform(lldb::ProcessSP arg) {
436     return python::SWIGBridge::ToSWIGWrapper(arg);
437   }
438 
439   python::PythonObject Transform(lldb::ThreadPlanSP arg) {
440     return python::SWIGBridge::ToSWIGWrapper(arg);
441   }
442 
443   python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
444     return python::SWIGBridge::ToSWIGWrapper(arg);
445   }
446 
447   python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
448     return python::SWIGBridge::ToSWIGWrapper(arg);
449   }
450 
451   python::PythonObject Transform(Event *arg) {
452     return python::SWIGBridge::ToSWIGWrapper(arg);
453   }
454 
455   python::PythonObject Transform(lldb::StreamSP arg) {
456     return python::SWIGBridge::ToSWIGWrapper(arg.get());
457   }
458 
459   python::PythonObject Transform(lldb::DataExtractorSP arg) {
460     return python::SWIGBridge::ToSWIGWrapper(arg);
461   }
462 
463   template <typename T, typename U>
464   void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
465     // If U is not a PythonObject, don't touch it!
466     return;
467   }
468 
469   template <typename T>
470   void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
471                         Status &error) {
472     original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
473   }
474 
475   void ReverseTransform(bool &original_arg,
476                         python::PythonObject transformed_arg, Status &error) {
477     python::PythonBoolean boolean_arg = python::PythonBoolean(
478         python::PyRefType::Borrowed, transformed_arg.get());
479     if (boolean_arg.IsValid())
480       original_arg = boolean_arg.GetValue();
481     else
482       error = Status::FromErrorStringWithFormatv(
483           "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION);
484   }
485 
486   template <std::size_t... I, typename... Args>
487   auto TransformTuple(const std::tuple<Args...> &args,
488                       std::index_sequence<I...>) {
489     return std::make_tuple(Transform(std::get<I>(args))...);
490   }
491 
492   // This will iterate over the Dispatch parameter pack and replace in-place
493   // every `lldb_private` argument that has a SB counterpart.
494   template <typename... Args>
495   auto TransformArgs(const std::tuple<Args...> &args) {
496     return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
497   }
498 
499   template <typename T, typename U>
500   void TransformBack(T &original_arg, U transformed_arg, Status &error) {
501     ReverseTransform(original_arg, transformed_arg, error);
502   }
503 
504   template <std::size_t... I, typename... Ts, typename... Us>
505   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
506                               std::tuple<Us...> &transformed_args,
507                               std::index_sequence<I...>) {
508     Status error;
509     (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
510                    error),
511      ...);
512     return error.Success();
513   }
514 
515   template <typename... Ts, typename... Us>
516   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
517                               std::tuple<Us...> &transformed_args) {
518     if (sizeof...(Ts) != sizeof...(Us))
519       return false;
520 
521     return ReassignPtrsOrRefsArgs(original_args, transformed_args,
522                                   std::make_index_sequence<sizeof...(Ts)>());
523   }
524 
525   template <typename T, typename... Args>
526   void FormatArgs(std::string &fmt, T arg, Args... args) const {
527     FormatArgs(fmt, arg);
528     FormatArgs(fmt, args...);
529   }
530 
531   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
532     fmt += python::PythonFormat<T>::format;
533   }
534 
535   void FormatArgs(std::string &fmt) const {}
536 
537   // The lifetime is managed by the ScriptInterpreter
538   ScriptInterpreterPythonImpl &m_interpreter;
539 };
540 
541 template <>
542 StructuredData::ArraySP
543 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
544     python::PythonObject &p, Status &error);
545 
546 template <>
547 StructuredData::DictionarySP
548 ScriptedPythonInterface::ExtractValueFromPythonObject<
549     StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
550 
551 template <>
552 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
553     python::PythonObject &p, Status &error);
554 
555 template <>
556 Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
557     python::PythonObject &p, Status &error);
558 
559 template <>
560 lldb::StreamSP
561 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
562     python::PythonObject &p, Status &error);
563 
564 template <>
565 lldb::BreakpointSP
566 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
567     python::PythonObject &p, Status &error);
568 
569 template <>
570 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
571     lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
572 
573 template <>
574 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
575     lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
576 
577 template <>
578 lldb::DataExtractorSP
579 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
580     python::PythonObject &p, Status &error);
581 
582 template <>
583 std::optional<MemoryRegionInfo>
584 ScriptedPythonInterface::ExtractValueFromPythonObject<
585     std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
586 
587 template <>
588 lldb::ExecutionContextRefSP
589 ScriptedPythonInterface::ExtractValueFromPythonObject<
590     lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);
591 
592 } // namespace lldb_private
593 
594 #endif // LLDB_ENABLE_PYTHON
595 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
596