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