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
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(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
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 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 Transform(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
416template <>
417StructuredData::ArraySP
418ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
419 python::PythonObject &p, Status &error);
420
421template <>
422StructuredData::DictionarySP
423ScriptedPythonInterface::ExtractValueFromPythonObject<
424 StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
425
426template <>
427Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
428 python::PythonObject &p, Status &error);
429
430template <>
431lldb::BreakpointSP
432ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
433 python::PythonObject &p, Status &error);
434
435template <>
436lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
437 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
438
439template <>
440lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
441 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
442
443template <>
444lldb::DataExtractorSP
445ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
446 python::PythonObject &p, Status &error);
447
448template <>
449std::optional<MemoryRegionInfo>
450ScriptedPythonInterface::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

source code of lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h