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 eUnknownArgumentCount,
40 eInvalidArgumentCount,
41 eValid
42 };
43
44 struct AbstrackMethodCheckerPayload {
45
46 struct InvalidArgumentCountPayload {
47 InvalidArgumentCountPayload(size_t required, size_t actual)
48 : required_argument_count(required), actual_argument_count(actual) {}
49
50 size_t required_argument_count;
51 size_t actual_argument_count;
52 };
53
54 AbstractMethodCheckerCases checker_case;
55 std::variant<std::monostate, InvalidArgumentCountPayload> payload;
56 };
57
58 llvm::Expected<std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload>>
59 CheckAbstractMethodImplementation(
60 const python::PythonDictionary &class_dict) const {
61
62 using namespace python;
63
64 std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload> checker;
65#define SET_CASE_AND_CONTINUE(method_name, case) \
66 { \
67 checker[method_name] = {case, {}}; \
68 continue; \
69 }
70
71 for (const AbstractMethodRequirement &requirement :
72 GetAbstractMethodRequirements()) {
73 llvm::StringLiteral method_name = requirement.name;
74 if (!class_dict.HasKey(key: method_name))
75 SET_CASE_AND_CONTINUE(method_name,
76 AbstractMethodCheckerCases::eNotImplemented)
77 auto callable_or_err = class_dict.GetItem(key: method_name);
78 if (!callable_or_err) {
79 llvm::consumeError(Err: callable_or_err.takeError());
80 SET_CASE_AND_CONTINUE(method_name,
81 AbstractMethodCheckerCases::eNotAllocated)
82 }
83
84 PythonCallable callable = callable_or_err->AsType<PythonCallable>();
85 if (!callable)
86 SET_CASE_AND_CONTINUE(method_name,
87 AbstractMethodCheckerCases::eNotCallable)
88
89 if (!requirement.min_arg_count)
90 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)
91
92 auto arg_info_or_err = callable.GetArgInfo();
93 if (!arg_info_or_err) {
94 llvm::consumeError(Err: arg_info_or_err.takeError());
95 SET_CASE_AND_CONTINUE(method_name,
96 AbstractMethodCheckerCases::eUnknownArgumentCount)
97 }
98
99 PythonCallable::ArgInfo arg_info = *arg_info_or_err;
100 if (requirement.min_arg_count <= arg_info.max_positional_args) {
101 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)
102 } else {
103 checker[method_name] = {
104 .checker_case: AbstractMethodCheckerCases::eInvalidArgumentCount,
105 .payload: AbstrackMethodCheckerPayload::InvalidArgumentCountPayload(
106 requirement.min_arg_count, arg_info.max_positional_args)};
107 }
108 }
109
110#undef SET_CASE_AND_CONTINUE
111
112 return checker;
113 }
114
115 template <typename... Args>
116 llvm::Expected<StructuredData::GenericSP>
117 CreatePluginObject(llvm::StringRef class_name,
118 StructuredData::Generic *script_obj, Args... args) {
119 using namespace python;
120 using Locker = ScriptInterpreterPythonImpl::Locker;
121
122 Log *log = GetLog(mask: LLDBLog::Script);
123 auto create_error = [](llvm::StringLiteral format, auto &&...ts) {
124 return llvm::createStringError(
125 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)
126 .str());
127 };
128
129 bool has_class_name = !class_name.empty();
130 bool has_interpreter_dict =
131 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());
132 if (!has_class_name && !has_interpreter_dict && !script_obj) {
133 if (!has_class_name)
134 return create_error("Missing script class name.");
135 else if (!has_interpreter_dict)
136 return create_error("Invalid script interpreter dictionary.");
137 else
138 return create_error("Missing scripting object.");
139 }
140
141 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
142 Locker::FreeLock);
143
144 PythonObject result = {};
145
146 if (script_obj) {
147 result = PythonObject(PyRefType::Borrowed,
148 static_cast<PyObject *>(script_obj->GetValue()));
149 } else {
150 auto dict =
151 PythonModule::MainModule().ResolveName<python::PythonDictionary>(
152 name: m_interpreter.GetDictionaryName());
153 if (!dict.IsAllocated())
154 return create_error("Could not find interpreter dictionary: {0}",
155 m_interpreter.GetDictionaryName());
156
157 auto init =
158 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
159 name: class_name, dict);
160 if (!init.IsAllocated())
161 return create_error("Could not find script class: {0}",
162 class_name.data());
163
164 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
165 auto transformed_args = TransformArgs(original_args);
166
167 std::string error_string;
168 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();
169 if (!arg_info) {
170 llvm::handleAllErrors(
171 arg_info.takeError(),
172 [&](PythonException &E) { error_string.append(str: E.ReadBacktrace()); },
173 [&](const llvm::ErrorInfoBase &E) {
174 error_string.append(str: E.message());
175 });
176 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
177 S: error_string);
178 }
179
180 llvm::Expected<PythonObject> expected_return_object =
181 create_error("Resulting object is not initialized.");
182
183 // This relax the requirement on the number of argument for
184 // initializing scripting extension if the size of the interface
185 // parameter pack contains 1 less element than the extension maximum
186 // number of positional arguments for this initializer.
187 //
188 // This addresses the cases where the embedded interpreter session
189 // dictionary is passed to the extension initializer which is not used
190 // most of the time.
191 size_t num_args = sizeof...(Args);
192 if (num_args != arg_info->max_positional_args) {
193 if (num_args != arg_info->max_positional_args - 1)
194 return create_error("Passed arguments ({0}) doesn't match the number "
195 "of expected arguments ({1}).",
196 num_args, arg_info->max_positional_args);
197
198 std::apply(
199 [&init, &expected_return_object](auto &&...args) {
200 llvm::consumeError(Err: expected_return_object.takeError());
201 expected_return_object = init(args...);
202 },
203 std::tuple_cat(transformed_args, std::make_tuple(args&: dict)));
204 } else {
205 std::apply(
206 [&init, &expected_return_object](auto &&...args) {
207 llvm::consumeError(Err: expected_return_object.takeError());
208 expected_return_object = init(args...);
209 },
210 transformed_args);
211 }
212
213 if (!expected_return_object)
214 return expected_return_object.takeError();
215 result = expected_return_object.get();
216 }
217
218 if (!result.IsValid())
219 return create_error("Resulting object is not a valid Python Object.");
220 if (!result.HasAttribute(attribute: "__class__"))
221 return create_error("Resulting object doesn't have '__class__' member.");
222
223 PythonObject obj_class = result.GetAttributeValue(attribute: "__class__");
224 if (!obj_class.IsValid())
225 return create_error("Resulting class object is not a valid.");
226 if (!obj_class.HasAttribute(attribute: "__name__"))
227 return create_error(
228 "Resulting object class doesn't have '__name__' member.");
229 PythonString obj_class_name =
230 obj_class.GetAttributeValue(attribute: "__name__").AsType<PythonString>();
231
232 PythonObject object_class_mapping_proxy =
233 obj_class.GetAttributeValue(attribute: "__dict__");
234 if (!obj_class.HasAttribute(attribute: "__dict__"))
235 return create_error(
236 "Resulting object class doesn't have '__dict__' member.");
237
238 PythonCallable dict_converter = PythonModule::BuiltinsModule()
239 .ResolveName(name: "dict")
240 .AsType<PythonCallable>();
241 if (!dict_converter.IsAllocated())
242 return create_error(
243 "Python 'builtins' module doesn't have 'dict' class.");
244
245 PythonDictionary object_class_dict =
246 dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();
247 if (!object_class_dict.IsAllocated())
248 return create_error("Coudn't create dictionary from resulting object "
249 "class mapping proxy object.");
250
251 auto checker_or_err = CheckAbstractMethodImplementation(class_dict: object_class_dict);
252 if (!checker_or_err)
253 return checker_or_err.takeError();
254
255 llvm::Error abstract_method_errors = llvm::Error::success();
256 for (const auto &method_checker : *checker_or_err)
257 switch (method_checker.second.checker_case) {
258 case AbstractMethodCheckerCases::eNotImplemented:
259 abstract_method_errors = llvm::joinErrors(
260 E1: std::move(abstract_method_errors),
261 E2: std::move(create_error("Abstract method {0}.{1} not implemented.",
262 obj_class_name.GetString(),
263 method_checker.first)));
264 break;
265 case AbstractMethodCheckerCases::eNotAllocated:
266 abstract_method_errors = llvm::joinErrors(
267 E1: std::move(abstract_method_errors),
268 E2: std::move(create_error("Abstract method {0}.{1} not allocated.",
269 obj_class_name.GetString(),
270 method_checker.first)));
271 break;
272 case AbstractMethodCheckerCases::eNotCallable:
273 abstract_method_errors = llvm::joinErrors(
274 E1: std::move(abstract_method_errors),
275 E2: std::move(create_error("Abstract method {0}.{1} not callable.",
276 obj_class_name.GetString(),
277 method_checker.first)));
278 break;
279 case AbstractMethodCheckerCases::eUnknownArgumentCount:
280 abstract_method_errors = llvm::joinErrors(
281 E1: std::move(abstract_method_errors),
282 E2: std::move(create_error(
283 "Abstract method {0}.{1} has unknown argument count.",
284 obj_class_name.GetString(), method_checker.first)));
285 break;
286 case AbstractMethodCheckerCases::eInvalidArgumentCount: {
287 auto &payload_variant = method_checker.second.payload;
288 if (!std::holds_alternative<
289 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
290 v: payload_variant)) {
291 abstract_method_errors = llvm::joinErrors(
292 E1: std::move(abstract_method_errors),
293 E2: std::move(create_error(
294 "Abstract method {0}.{1} has unexpected argument count.",
295 obj_class_name.GetString(), method_checker.first)));
296 } else {
297 auto payload = std::get<
298 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
299 v: payload_variant);
300 abstract_method_errors = llvm::joinErrors(
301 E1: std::move(abstract_method_errors),
302 E2: std::move(
303 create_error("Abstract method {0}.{1} has unexpected "
304 "argument count (expected {2} but has {3}).",
305 obj_class_name.GetString(), method_checker.first,
306 payload.required_argument_count,
307 payload.actual_argument_count)));
308 }
309 } break;
310 case AbstractMethodCheckerCases::eValid:
311 LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",
312 obj_class_name.GetString(), method_checker.first);
313 break;
314 }
315
316 if (abstract_method_errors) {
317 Status error = Status::FromError(error: std::move(abstract_method_errors));
318 LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name,
319 error.AsCString());
320 return error.ToError();
321 }
322
323 m_object_instance_sp = StructuredData::GenericSP(
324 new StructuredPythonObject(std::move(result)));
325 return m_object_instance_sp;
326 }
327
328protected:
329 template <typename T = StructuredData::ObjectSP>
330 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
331 return p.CreateStructuredObject();
332 }
333
334 template <typename T = StructuredData::ObjectSP, typename... Args>
335 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
336 using namespace python;
337 using Locker = ScriptInterpreterPythonImpl::Locker;
338
339 std::string caller_signature =
340 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
341 llvm::Twine(method_name) + llvm::Twine(")"))
342 .str();
343 if (!m_object_instance_sp)
344 return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",
345 error);
346
347 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
348 Locker::FreeLock);
349
350 PythonObject implementor(PyRefType::Borrowed,
351 (PyObject *)m_object_instance_sp->GetValue());
352
353 if (!implementor.IsAllocated())
354 return llvm::is_contained(Range: GetAbstractMethods(), Element: method_name)
355 ? ErrorWithMessage<T>(caller_signature,
356 "Python implementor not allocated.",
357 error)
358 : T{};
359
360 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
361 auto transformed_args = TransformArgs(original_args);
362
363 llvm::Expected<PythonObject> expected_return_object =
364 llvm::make_error<llvm::StringError>(Args: "Not initialized.",
365 Args: llvm::inconvertibleErrorCode());
366 std::apply(
367 [&implementor, &method_name, &expected_return_object](auto &&...args) {
368 llvm::consumeError(Err: expected_return_object.takeError());
369 expected_return_object =
370 implementor.CallMethod(method_name.data(), args...);
371 },
372 transformed_args);
373
374 if (llvm::Error e = expected_return_object.takeError()) {
375 error = Status::FromError(error: std::move(e));
376 return ErrorWithMessage<T>(caller_signature,
377 "Python method could not be called.", error);
378 }
379
380 PythonObject py_return = std::move(expected_return_object.get());
381
382 // Now that we called the python method with the transformed arguments,
383 // we need to interate again over both the original and transformed
384 // parameter pack, and transform back the parameter that were passed in
385 // the original parameter pack as references or pointers.
386 if (sizeof...(Args) > 0)
387 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
388 return ErrorWithMessage<T>(
389 caller_signature,
390 "Couldn't re-assign reference and pointer arguments.", error);
391
392 if (!py_return.IsAllocated())
393 return {};
394 return ExtractValueFromPythonObject<T>(py_return, error);
395 }
396
397 template <typename... Args>
398 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
399 Status error;
400 Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
401
402 return error;
403 }
404
405 template <typename T> T Transform(T object) {
406 // No Transformation for generic usage
407 return {object};
408 }
409
410 python::PythonObject Transform(bool arg) {
411 // Boolean arguments need to be turned into python objects.
412 return python::PythonBoolean(arg);
413 }
414
415 python::PythonObject Transform(const Status &arg) {
416 return python::SWIGBridge::ToSWIGWrapper(status: arg.Clone());
417 }
418
419 python::PythonObject Transform(Status &&arg) {
420 return python::SWIGBridge::ToSWIGWrapper(status: std::move(arg));
421 }
422
423 python::PythonObject Transform(const StructuredDataImpl &arg) {
424 return python::SWIGBridge::ToSWIGWrapper(data_impl: arg);
425 }
426
427 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {
428 return python::SWIGBridge::ToSWIGWrapper(ctx_sp: arg);
429 }
430
431 python::PythonObject Transform(lldb::TargetSP arg) {
432 return python::SWIGBridge::ToSWIGWrapper(target_sp: arg);
433 }
434
435 python::PythonObject Transform(lldb::ProcessSP arg) {
436 return python::SWIGBridge::ToSWIGWrapper(process_sp: arg);
437 }
438
439 python::PythonObject Transform(lldb::ThreadPlanSP arg) {
440 return python::SWIGBridge::ToSWIGWrapper(thread_plan_sp: arg);
441 }
442
443 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
444 return python::SWIGBridge::ToSWIGWrapper(attach_info_sp: arg);
445 }
446
447 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
448 return python::SWIGBridge::ToSWIGWrapper(launch_info_sp: arg);
449 }
450
451 python::PythonObject Transform(Event *arg) {
452 return python::SWIGBridge::ToSWIGWrapper(event: arg);
453 }
454
455 python::PythonObject Transform(lldb::StreamSP arg) {
456 return python::SWIGBridge::ToSWIGWrapper(stream: arg.get());
457 }
458
459 python::PythonObject Transform(lldb::DataExtractorSP arg) {
460 return python::SWIGBridge::ToSWIGWrapper(data_extractor_sp: arg);
461 }
462
463 template <typename T, typename U>
464 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
465 // If U is not a PythonObject, don't touch it!
466 }
467
468 template <typename T>
469 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
470 Status &error) {
471 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
472 }
473
474 void ReverseTransform(bool &original_arg,
475 python::PythonObject transformed_arg, Status &error) {
476 python::PythonBoolean boolean_arg = python::PythonBoolean(
477 python::PyRefType::Borrowed, transformed_arg.get());
478 if (boolean_arg.IsValid())
479 original_arg = boolean_arg.GetValue();
480 else
481 error = Status::FromErrorStringWithFormatv(
482 format: "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION);
483 }
484
485 template <std::size_t... I, typename... Args>
486 auto TransformTuple(const std::tuple<Args...> &args,
487 std::index_sequence<I...>) {
488 return std::make_tuple(Transform(std::get<I>(args))...);
489 }
490
491 // This will iterate over the Dispatch parameter pack and replace in-place
492 // every `lldb_private` argument that has a SB counterpart.
493 template <typename... Args>
494 auto TransformArgs(const std::tuple<Args...> &args) {
495 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
496 }
497
498 template <typename T, typename U>
499 void TransformBack(T &original_arg, U transformed_arg, Status &error) {
500 ReverseTransform(original_arg, transformed_arg, error);
501 }
502
503 template <std::size_t... I, typename... Ts, typename... Us>
504 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
505 std::tuple<Us...> &transformed_args,
506 std::index_sequence<I...>) {
507 Status error;
508 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
509 error),
510 ...);
511 return error.Success();
512 }
513
514 template <typename... Ts, typename... Us>
515 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
516 std::tuple<Us...> &transformed_args) {
517 if (sizeof...(Ts) != sizeof...(Us))
518 return false;
519
520 return ReassignPtrsOrRefsArgs(original_args, transformed_args,
521 std::make_index_sequence<sizeof...(Ts)>());
522 }
523
524 template <typename T, typename... Args>
525 void FormatArgs(std::string &fmt, T arg, Args... args) const {
526 FormatArgs(fmt, arg);
527 FormatArgs(fmt, args...);
528 }
529
530 template <typename T> void FormatArgs(std::string &fmt, T arg) const {
531 fmt += python::PythonFormat<T>::format;
532 }
533
534 void FormatArgs(std::string &fmt) const {}
535
536 // The lifetime is managed by the ScriptInterpreter
537 ScriptInterpreterPythonImpl &m_interpreter;
538};
539
540template <>
541StructuredData::ArraySP
542ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
543 python::PythonObject &p, Status &error);
544
545template <>
546StructuredData::DictionarySP
547ScriptedPythonInterface::ExtractValueFromPythonObject<
548 StructuredData::DictionarySP>(python::PythonObject &p, Status &error);
549
550template <>
551Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
552 python::PythonObject &p, Status &error);
553
554template <>
555Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
556 python::PythonObject &p, Status &error);
557
558template <>
559lldb::StreamSP
560ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
561 python::PythonObject &p, Status &error);
562
563template <>
564lldb::BreakpointSP
565ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
566 python::PythonObject &p, Status &error);
567
568template <>
569lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
570 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
571
572template <>
573lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
574 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);
575
576template <>
577lldb::DataExtractorSP
578ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
579 python::PythonObject &p, Status &error);
580
581template <>
582std::optional<MemoryRegionInfo>
583ScriptedPythonInterface::ExtractValueFromPythonObject<
584 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
585
586template <>
587lldb::ExecutionContextRefSP
588ScriptedPythonInterface::ExtractValueFromPythonObject<
589 lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);
590
591} // namespace lldb_private
592
593#endif // LLDB_ENABLE_PYTHON
594#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
595

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