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