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(key: method_name)) |
57 | SET_ERROR_AND_CONTINUE(method_name, |
58 | AbstractMethodCheckerCases::eNotImplemented) |
59 | auto callable_or_err = class_dict.GetItem(key: method_name); |
60 | if (!callable_or_err) |
61 | SET_ERROR_AND_CONTINUE(method_name, |
62 | AbstractMethodCheckerCases::eNotAllocated) |
63 | if (!PythonCallable::Check(py_obj: 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(EC: llvm::inconvertibleErrorCode(), S: 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 | name: m_interpreter.GetDictionaryName()); |
109 | if (!dict.IsAllocated()) |
110 | return create_error( |
111 | llvm::formatv(Fmt: "Could not find interpreter dictionary: %s" , |
112 | Vals: m_interpreter.GetDictionaryName())); |
113 | |
114 | auto init = |
115 | PythonObject::ResolveNameWithDictionary<python::PythonCallable>( |
116 | name: class_name, dict); |
117 | if (!init.IsAllocated()) |
118 | return create_error(llvm::formatv(Fmt: "Could not find script class: %s" , |
119 | Vals: 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(str: E.ReadBacktrace()); }, |
130 | [&](const llvm::ErrorInfoBase &E) { |
131 | error_string.append(str: E.message()); |
132 | }); |
133 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
134 | S: 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(Err: 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(attribute: "__class__" )) |
155 | return create_error("Resulting object doesn't have '__class__' member." ); |
156 | |
157 | PythonObject obj_class = result.GetAttributeValue(attribute: "__class__" ); |
158 | if (!obj_class.IsValid()) |
159 | return create_error("Resulting class object is not a valid." ); |
160 | if (!obj_class.HasAttribute(attribute: "__name__" )) |
161 | return create_error( |
162 | "Resulting object class doesn't have '__name__' member." ); |
163 | PythonString obj_class_name = |
164 | obj_class.GetAttributeValue(attribute: "__name__" ).AsType<PythonString>(); |
165 | |
166 | PythonObject object_class_mapping_proxy = |
167 | obj_class.GetAttributeValue(attribute: "__dict__" ); |
168 | if (!obj_class.HasAttribute(attribute: "__dict__" )) |
169 | return create_error( |
170 | "Resulting object class doesn't have '__dict__' member." ); |
171 | |
172 | PythonCallable dict_converter = PythonModule::BuiltinsModule() |
173 | .ResolveName(name: "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(class_dict: 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(Fmt: "Abstract method {0}.{1} missing. Enable lldb " |
217 | "script log for more details." , |
218 | Vals: obj_class_name.GetString(), Vals: 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> |
227 | T (python::PythonObject &p, Status &error) { |
228 | return p.CreateStructuredObject(); |
229 | } |
230 | |
231 | template <typename T = StructuredData::ObjectSP, typename... 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>(Args: "Not initialized." , |
259 | Args: llvm::inconvertibleErrorCode()); |
260 | std::apply( |
261 | [&implementor, &method_name, &expected_return_object](auto &&...args) { |
262 | llvm::consumeError(Err: 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(E: 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> |
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 | |
299 | template <typename T> T Transform(T object) { |
300 | // No Transformation for generic usage |
301 | return {object}; |
302 | } |
303 | |
304 | python::PythonObject Transform(bool arg) { |
305 | // Boolean arguments need to be turned into python objects. |
306 | return python::PythonBoolean(arg); |
307 | } |
308 | |
309 | python::PythonObject Transform(Status arg) { |
310 | return python::SWIGBridge::ToSWIGWrapper(status: arg); |
311 | } |
312 | |
313 | python::PythonObject Transform(const StructuredDataImpl &arg) { |
314 | return python::SWIGBridge::ToSWIGWrapper(data_impl: arg); |
315 | } |
316 | |
317 | python::PythonObject Transform(lldb::ExecutionContextRefSP arg) { |
318 | return python::SWIGBridge::ToSWIGWrapper(ctx_sp: arg); |
319 | } |
320 | |
321 | python::PythonObject Transform(lldb::ProcessSP arg) { |
322 | return python::SWIGBridge::ToSWIGWrapper(process_sp: arg); |
323 | } |
324 | |
325 | python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { |
326 | return python::SWIGBridge::ToSWIGWrapper(attach_info_sp: arg); |
327 | } |
328 | |
329 | python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { |
330 | return python::SWIGBridge::ToSWIGWrapper(launch_info_sp: arg); |
331 | } |
332 | |
333 | python::PythonObject (lldb::DataExtractorSP arg) { |
334 | return python::SWIGBridge::ToSWIGWrapper(data_extractor_sp: arg); |
335 | } |
336 | |
337 | template <typename T, typename U> |
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> |
344 | void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, |
345 | Status &error) { |
346 | original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); |
347 | } |
348 | |
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(Fmt: "{}: Invalid boolean argument." , LLVM_PRETTY_FUNCTION) |
358 | .str()); |
359 | } |
360 | |
361 | template <std::size_t... I, typename... Args> |
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> |
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> |
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> |
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> |
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> |
401 | void FormatArgs(std::string &fmt, T arg, Args... args) const { |
402 | FormatArgs(fmt, arg); |
403 | FormatArgs(fmt, args...); |
404 | } |
405 | |
406 | template <typename T> void FormatArgs(std::string &fmt, T arg) const { |
407 | fmt += python::PythonFormat<T>::format; |
408 | } |
409 | |
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 | |