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