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_SCRIPTEDPYTHONINTERFACE_H
10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_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/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 protected:
36   template <typename T = StructuredData::ObjectSP>
ExtractValueFromPythonObject(python::PythonObject & p,Status & error)37   T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
38     return p.CreateStructuredObject();
39   }
40 
41   template <typename T = StructuredData::ObjectSP, typename... Args>
Dispatch(llvm::StringRef method_name,Status & error,Args &&...args)42   T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
43     using namespace python;
44     using Locker = ScriptInterpreterPythonImpl::Locker;
45 
46     std::string caller_signature =
47         llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
48                     llvm::Twine(method_name) + llvm::Twine(")"))
49             .str();
50     if (!m_object_instance_sp)
51       return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",
52                                  error);
53 
54     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
55                    Locker::FreeLock);
56 
57     PythonObject implementor(PyRefType::Borrowed,
58                              (PyObject *)m_object_instance_sp->GetValue());
59 
60     if (!implementor.IsAllocated())
61       return ErrorWithMessage<T>(caller_signature,
62                                  "Python implementor not allocated.", error);
63 
64     std::tuple<Args...> original_args = std::forward_as_tuple(args...);
65     auto transformed_args = TransformArgs(original_args);
66 
67     llvm::Expected<PythonObject> expected_return_object =
68         llvm::make_error<llvm::StringError>("Not initialized.",
69                                             llvm::inconvertibleErrorCode());
70     std::apply(
71         [&implementor, &method_name, &expected_return_object](auto &&...args) {
72           llvm::consumeError(expected_return_object.takeError());
73           expected_return_object =
74               implementor.CallMethod(method_name.data(), args...);
75         },
76         transformed_args);
77 
78     if (llvm::Error e = expected_return_object.takeError()) {
79       error.SetErrorString(llvm::toString(std::move(e)).c_str());
80       return ErrorWithMessage<T>(caller_signature,
81                                  "Python method could not be called.", error);
82     }
83 
84     PythonObject py_return = std::move(expected_return_object.get());
85 
86     if (!py_return.IsAllocated())
87       return ErrorWithMessage<T>(caller_signature, "Returned object is null.",
88                                  error);
89 
90     // Now that we called the python method with the transformed arguments,
91     // we need to interate again over both the original and transformed
92     // parameter pack, and transform back the parameter that were passed in
93     // the original parameter pack as references or pointers.
94     if (sizeof...(Args) > 0)
95       if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
96         return ErrorWithMessage<T>(
97             caller_signature,
98             "Couldn't re-assign reference and pointer arguments.", error);
99 
100     return ExtractValueFromPythonObject<T>(py_return, error);
101   }
102 
103   template <typename... Args>
GetStatusFromMethod(llvm::StringRef method_name,Args &&...args)104   Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
105     Status error;
106     Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
107 
108     return error;
109   }
110 
Transform(T object)111   template <typename T> T Transform(T object) {
112     // No Transformation for generic usage
113     return {object};
114   }
115 
Transform(Status arg)116   python::PythonObject Transform(Status arg) {
117     return python::ToSWIGWrapper(arg);
118   }
119 
120   template <typename T, typename U>
ReverseTransform(T & original_arg,U transformed_arg,Status & error)121   void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
122     // If U is not a PythonObject, don't touch it!
123     return;
124   }
125 
126   template <typename T>
ReverseTransform(T & original_arg,python::PythonObject transformed_arg,Status & error)127   void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
128                         Status &error) {
129     original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
130   }
131 
132   template <std::size_t... I, typename... Args>
TransformTuple(const std::tuple<Args...> & args,std::index_sequence<I...>)133   auto TransformTuple(const std::tuple<Args...> &args,
134                       std::index_sequence<I...>) {
135     return std::make_tuple(Transform(std::get<I>(args))...);
136   }
137 
138   // This will iterate over the Dispatch parameter pack and replace in-place
139   // every `lldb_private` argument that has a SB counterpart.
140   template <typename... Args>
TransformArgs(const std::tuple<Args...> & args)141   auto TransformArgs(const std::tuple<Args...> &args) {
142     return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
143   }
144 
145   template <typename T, typename U>
TransformBack(T & original_arg,U transformed_arg,Status & error)146   void TransformBack(T &original_arg, U transformed_arg, Status &error) {
147     ReverseTransform(original_arg, transformed_arg, error);
148   }
149 
150   template <std::size_t... I, typename... Ts, typename... Us>
ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args,std::index_sequence<I...>)151   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
152                               std::tuple<Us...> &transformed_args,
153                               std::index_sequence<I...>) {
154     Status error;
155     (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
156                    error),
157      ...);
158     return error.Success();
159   }
160 
161   template <typename... Ts, typename... Us>
ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args)162   bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
163                               std::tuple<Us...> &transformed_args) {
164     if (sizeof...(Ts) != sizeof...(Us))
165       return false;
166 
167     return ReassignPtrsOrRefsArgs(original_args, transformed_args,
168                                   std::make_index_sequence<sizeof...(Ts)>());
169   }
170 
171   template <typename T, typename... Args>
FormatArgs(std::string & fmt,T arg,Args...args)172   void FormatArgs(std::string &fmt, T arg, Args... args) const {
173     FormatArgs(fmt, arg);
174     FormatArgs(fmt, args...);
175   }
176 
FormatArgs(std::string & fmt,T arg)177   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
178     fmt += python::PythonFormat<T>::format;
179   }
180 
FormatArgs(std::string & fmt)181   void FormatArgs(std::string &fmt) const {}
182 
183   // The lifetime is managed by the ScriptInterpreter
184   ScriptInterpreterPythonImpl &m_interpreter;
185 };
186 
187 template <>
188 StructuredData::ArraySP
189 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
190     python::PythonObject &p, Status &error);
191 
192 template <>
193 StructuredData::DictionarySP
194 ScriptedPythonInterface::ExtractValueFromPythonObject<
195     StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
196 
197 template <>
198 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
199     python::PythonObject &p, Status &error);
200 
201 template <>
202 lldb::DataExtractorSP
203 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
204     python::PythonObject &p, Status &error);
205 
206 template <>
207 std::optional<MemoryRegionInfo>
208 ScriptedPythonInterface::ExtractValueFromPythonObject<
209     std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
210 
211 } // namespace lldb_private
212 
213 #endif // LLDB_ENABLE_PYTHON
214 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
215