LLDB mainline
ScriptedPythonInterface.h
Go to the documentation of this file.
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"
23
24#include "../PythonDataObjects.h"
25#include "../SWIGPythonBridge.h"
26#include "../ScriptInterpreterPythonImpl.h"
27
28namespace lldb_private {
29class ScriptInterpreterPythonImpl;
30class ScriptedPythonInterface : virtual public ScriptedInterface {
31public:
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(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>
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: {0}",
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
225protected:
226 template <typename T = StructuredData::ObjectSP>
227 T ExtractValueFromPythonObject(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 llvm::is_contained(GetAbstractMethods(), method_name)
252 ? ErrorWithMessage<T>(caller_signature,
253 "Python implementor not allocated.",
254 error)
255 : T{};
256
257 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
258 auto transformed_args = TransformArgs(original_args);
259
260 llvm::Expected<PythonObject> expected_return_object =
261 llvm::make_error<llvm::StringError>("Not initialized.",
262 llvm::inconvertibleErrorCode());
263 std::apply(
264 [&implementor, &method_name, &expected_return_object](auto &&...args) {
265 llvm::consumeError(expected_return_object.takeError());
266 expected_return_object =
267 implementor.CallMethod(method_name.data(), args...);
268 },
269 transformed_args);
270
271 if (llvm::Error e = expected_return_object.takeError()) {
272 error.SetErrorString(llvm::toString(std::move(e)).c_str());
273 return ErrorWithMessage<T>(caller_signature,
274 "Python method could not be called.", error);
275 }
276
277 PythonObject py_return = std::move(expected_return_object.get());
278
279 // Now that we called the python method with the transformed arguments,
280 // we need to interate again over both the original and transformed
281 // parameter pack, and transform back the parameter that were passed in
282 // the original parameter pack as references or pointers.
283 if (sizeof...(Args) > 0)
284 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
285 return ErrorWithMessage<T>(
286 caller_signature,
287 "Couldn't re-assign reference and pointer arguments.", error);
288
289 if (!py_return.IsAllocated())
290 return {};
291 return ExtractValueFromPythonObject<T>(py_return, error);
292 }
293
294 template <typename... Args>
295 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
297 Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
298
299 return error;
300 }
301
302 template <typename T> T Transform(T object) {
303 // No Transformation for generic usage
304 return {object};
305 }
306
307 python::PythonObject Transform(bool arg) {
308 // Boolean arguments need to be turned into python objects.
309 return python::PythonBoolean(arg);
310 }
311
312 python::PythonObject Transform(Status arg) {
313 return python::SWIGBridge::ToSWIGWrapper(arg);
314 }
315
316 python::PythonObject Transform(const StructuredDataImpl &arg) {
317 return python::SWIGBridge::ToSWIGWrapper(arg);
318 }
319
320 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {
321 return python::SWIGBridge::ToSWIGWrapper(arg);
322 }
323
324 python::PythonObject Transform(lldb::ProcessSP arg) {
325 return python::SWIGBridge::ToSWIGWrapper(arg);
326 }
327
328 python::PythonObject Transform(lldb::ThreadPlanSP arg) {
329 return python::SWIGBridge::ToSWIGWrapper(arg);
330 }
331
332 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
333 return python::SWIGBridge::ToSWIGWrapper(arg);
334 }
335
336 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
337 return python::SWIGBridge::ToSWIGWrapper(arg);
338 }
339
340 python::PythonObject Transform(Event *arg) {
341 return python::SWIGBridge::ToSWIGWrapper(arg);
342 }
343
344 python::PythonObject Transform(lldb::StreamSP arg) {
345 return python::SWIGBridge::ToSWIGWrapper(arg.get());
346 }
347
348 python::PythonObject Transform(lldb::DataExtractorSP arg) {
349 return python::SWIGBridge::ToSWIGWrapper(arg);
350 }
351
352 template <typename T, typename U>
353 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
354 // If U is not a PythonObject, don't touch it!
355 return;
356 }
357
358 template <typename T>
359 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
360 Status &error) {
361 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
362 }
363
364 void ReverseTransform(bool &original_arg,
365 python::PythonObject transformed_arg, Status &error) {
366 python::PythonBoolean boolean_arg = python::PythonBoolean(
367 python::PyRefType::Borrowed, transformed_arg.get());
368 if (boolean_arg.IsValid())
369 original_arg = boolean_arg.GetValue();
370 else
371 error.SetErrorString(
372 llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION)
373 .str());
374 }
375
376 template <std::size_t... I, typename... Args>
377 auto TransformTuple(const std::tuple<Args...> &args,
378 std::index_sequence<I...>) {
379 return std::make_tuple(Transform(std::get<I>(args))...);
380 }
381
382 // This will iterate over the Dispatch parameter pack and replace in-place
383 // every `lldb_private` argument that has a SB counterpart.
384 template <typename... Args>
385 auto TransformArgs(const std::tuple<Args...> &args) {
386 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
387 }
388
389 template <typename T, typename U>
390 void TransformBack(T &original_arg, U transformed_arg, Status &error) {
391 ReverseTransform(original_arg, transformed_arg, error);
392 }
393
394 template <std::size_t... I, typename... Ts, typename... Us>
395 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
396 std::tuple<Us...> &transformed_args,
397 std::index_sequence<I...>) {
399 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
400 error),
401 ...);
402 return error.Success();
403 }
404
405 template <typename... Ts, typename... Us>
406 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
407 std::tuple<Us...> &transformed_args) {
408 if (sizeof...(Ts) != sizeof...(Us))
409 return false;
410
411 return ReassignPtrsOrRefsArgs(original_args, transformed_args,
412 std::make_index_sequence<sizeof...(Ts)>());
413 }
414
415 template <typename T, typename... Args>
416 void FormatArgs(std::string &fmt, T arg, Args... args) const {
417 FormatArgs(fmt, arg);
418 FormatArgs(fmt, args...);
419 }
420
421 template <typename T> void FormatArgs(std::string &fmt, T arg) const {
422 fmt += python::PythonFormat<T>::format;
423 }
424
425 void FormatArgs(std::string &fmt) const {}
426
427 // The lifetime is managed by the ScriptInterpreter
428 ScriptInterpreterPythonImpl &m_interpreter;
429};
430
431template <>
433ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
434 python::PythonObject &p, Status &error);
435
436template <>
438ScriptedPythonInterface::ExtractValueFromPythonObject<
439 StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
440
441template <>
442Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
443 python::PythonObject &p, Status &error);
444
445template <>
446Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
447 python::PythonObject &p, Status &error);
448
449template <>
451ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
452 python::PythonObject &p, Status &error);
453
454template <>
456ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
457 python::PythonObject &p, Status &error);
458
459template <>
460lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
461 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
462
463template <>
464lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
465 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
466
467template <>
469ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
470 python::PythonObject &p, Status &error);
471
472template <>
473std::optional<MemoryRegionInfo>
474ScriptedPythonInterface::ExtractValueFromPythonObject<
475 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
476
477} // namespace lldb_private
478
479#endif // LLDB_ENABLE_PYTHON
480#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:359
std::shared_ptr< Dictionary > DictionarySP
std::shared_ptr< Array > ArraySP
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:331
std::shared_ptr< lldb_private::ThreadPlan > ThreadPlanSP
Definition: lldb-forward.h:449
std::shared_ptr< lldb_private::ProcessAttachInfo > ProcessAttachInfoSP
Definition: lldb-forward.h:388
std::shared_ptr< lldb_private::Stream > StreamSP
Definition: lldb-forward.h:428
std::shared_ptr< lldb_private::Breakpoint > BreakpointSP
Definition: lldb-forward.h:319
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:387
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
Definition: lldb-forward.h:336
std::shared_ptr< lldb_private::ProcessLaunchInfo > ProcessLaunchInfoSP
Definition: lldb-forward.h:389
std::shared_ptr< lldb_private::ExecutionContextRef > ExecutionContextRefSP
Definition: lldb-forward.h:348