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