[Go to site: main page, start]

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_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
10#define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
11
12#include <optional>
13#include <sstream>
14#include <tuple>
15#include <type_traits>
16#include <utility>
17
21
23#include "../SWIGPythonBridge.h"
25
26namespace lldb_private {
29public:
31 ~ScriptedPythonInterface() override = default;
32
41
43
51
53 std::variant<std::monostate, InvalidArgumentCountPayload, std::string>
55 };
56
57 llvm::Expected<FileSpec> GetScriptedModulePath() override {
58 using namespace python;
60
63
65 return llvm::createStringError("scripted Interface has invalid object");
66
67 PythonObject py_obj =
68 PythonObject(PyRefType::Borrowed,
69 static_cast<PyObject *>(m_object_instance_sp->GetValue()));
70
71 if (!py_obj.IsAllocated())
72 return llvm::createStringError(
73 "scripted Interface has invalid python object");
74
75 PythonObject py_obj_class = py_obj.GetAttributeValue("__class__");
76 if (!py_obj_class.IsValid())
77 return llvm::createStringError(
78 "scripted Interface python object is missing '__class__' attribute");
79
80 PythonObject py_obj_module = py_obj_class.GetAttributeValue("__module__");
81 if (!py_obj_module.IsValid())
82 return llvm::createStringError(
83 "scripted Interface python object '__class__' is missing "
84 "'__module__' attribute");
85
86 PythonString py_obj_module_str = py_obj_module.Str();
87 if (!py_obj_module_str.IsValid())
88 return llvm::createStringError(
89 "scripted Interface python object '__class__.__module__' attribute "
90 "is not a string");
91
92 llvm::StringRef py_obj_module_str_ref = py_obj_module_str.GetString();
93 PythonModule py_module = PythonModule::AddModule(py_obj_module_str_ref);
94 if (!py_module.IsValid())
95 return llvm::createStringError("failed to import '%s' module",
96 py_obj_module_str_ref.data());
97
98 PythonObject py_module_file = py_module.GetAttributeValue("__file__");
99 if (!py_module_file.IsValid())
100 return llvm::createStringError(
101 "module '%s' is missing '__file__' attribute",
102 py_obj_module_str_ref.data());
103
104 PythonString py_module_file_str = py_module_file.Str();
105 if (!py_module_file_str.IsValid())
106 return llvm::createStringError(
107 "module '%s.__file__' attribute is not a string",
108 py_obj_module_str_ref.data());
109
110 return FileSpec(py_module_file_str.GetString());
111 }
112
113 llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerPayload>>
115 const python::PythonObject &obj_class) const {
116
117 using namespace python;
118
119 std::map<llvm::StringLiteral, AbstractMethodCheckerPayload> checker;
120#define SET_CASE_AND_CONTINUE(method_name, case) \
121 { \
122 checker[method_name] = {case, {}}; \
123 continue; \
124 }
125
126 for (const AbstractMethodRequirement &requirement :
128 llvm::StringLiteral method_name = requirement.name;
129 // Look up via attribute access so inherited methods are found; the
130 // class's own __dict__ omits anything defined on a base class.
131 if (!obj_class.HasAttribute(method_name))
132 SET_CASE_AND_CONTINUE(method_name,
134 PythonObject attr = obj_class.GetAttributeValue(method_name);
135 if (!attr.IsAllocated())
136 SET_CASE_AND_CONTINUE(method_name,
138
139 PythonCallable callable = attr.AsType<PythonCallable>();
140 if (!callable)
141 SET_CASE_AND_CONTINUE(method_name,
143
144 if (!requirement.min_arg_count)
146
147 auto arg_info_or_err = callable.GetArgInfo();
148 if (!arg_info_or_err) {
149 checker[method_name] = {
151 ExtractPythonError(arg_info_or_err.takeError())};
152 continue;
153 }
154
155 PythonCallable::ArgInfo arg_info = *arg_info_or_err;
156 if (requirement.min_arg_count <= arg_info.max_positional_args) {
158 } else {
159 checker[method_name] = {
162 requirement.min_arg_count, arg_info.max_positional_args)};
163 }
164 }
165
166#undef SET_CASE_AND_CONTINUE
167
168 return checker;
169 }
170
171 template <typename... Args>
172 llvm::Expected<StructuredData::GenericSP>
173 CreatePluginObject(const ScriptedMetadata &scripted_metadata,
174 StructuredData::Generic *script_obj, Args... args) {
175 using namespace python;
177
178 Log *log = GetLog(LLDBLog::Script);
179 auto create_error = [](llvm::StringLiteral format, auto &&...ts) {
180 return llvm::createStringError(
181 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)
182 .str());
183 };
184
185 m_scripted_metadata = scripted_metadata;
186 llvm::StringRef class_name = scripted_metadata.GetClassName();
187 bool has_class_name = !class_name.empty();
188 bool has_interpreter_dict =
189 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());
190 if (!has_class_name && !has_interpreter_dict && !script_obj) {
191 if (!has_class_name)
192 return create_error("Missing script class name.");
193 else if (!has_interpreter_dict)
194 return create_error("Invalid script interpreter dictionary.");
195 else
196 return create_error("Missing scripting object.");
197 }
198
201
202 PythonObject result = {};
203
204 if (script_obj) {
205 result = PythonObject(PyRefType::Borrowed,
206 static_cast<PyObject *>(script_obj->GetValue()));
207 } else {
208 auto dict =
209 PythonModule::MainModule().ResolveName<python::PythonDictionary>(
210 m_interpreter.GetDictionaryName());
211 if (!dict.IsAllocated())
212 return create_error("Could not find interpreter dictionary: {0}",
213 m_interpreter.GetDictionaryName());
214
215 auto init =
216 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
217 class_name, dict);
218 if (!init.IsAllocated())
219 return create_error("Could not find script class: {0}",
220 class_name.data());
221
222 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
223 auto transformed_args = TransformArgs(original_args);
224
225 std::string error_string;
226 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();
227 if (!arg_info) {
228 llvm::handleAllErrors(
229 arg_info.takeError(),
230 [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
231 [&](const llvm::ErrorInfoBase &E) {
232 error_string.append(E.message());
233 });
234 return llvm::createStringError(llvm::inconvertibleErrorCode(),
235 error_string);
236 }
237
238 llvm::Expected<PythonObject> expected_return_object =
239 create_error("Resulting object is not initialized.");
240
241 // This relax the requirement on the number of argument for
242 // initializing scripting extension if the size of the interface
243 // parameter pack contains 1 less element than the extension maximum
244 // number of positional arguments for this initializer.
245 //
246 // This addresses the cases where the embedded interpreter session
247 // dictionary is passed to the extension initializer which is not used
248 // most of the time.
249 // Note, though none of our API's suggest defining the interfaces with
250 // varargs, we have some extant clients that were doing that. To keep
251 // from breaking them, we just say putting a varargs in these signatures
252 // turns off argument checking.
253 size_t num_args = sizeof...(Args);
254 if (arg_info->max_positional_args != PythonCallable::ArgInfo::UNBOUNDED &&
255 num_args != arg_info->max_positional_args) {
256 if (num_args != arg_info->max_positional_args - 1) {
257 // `expected_return_object` starts in an error state; consume it
258 // before we return with a different error, or its destructor
259 // will abort.
260 llvm::consumeError(expected_return_object.takeError());
261 return create_error("Passed arguments ({0}) doesn't match the number "
262 "of expected arguments ({1}).",
263 num_args, arg_info->max_positional_args);
264 }
265
266 std::apply(
267 [&init, &expected_return_object](auto &&...args) {
268 if (!expected_return_object)
269 llvm::consumeError(expected_return_object.takeError());
270 expected_return_object = init.Call(args...);
271 },
272 std::tuple_cat(transformed_args, std::make_tuple(dict)));
273 } else {
274 std::apply(
275 [&init, &expected_return_object](auto &&...args) {
276 if (!expected_return_object)
277 llvm::consumeError(expected_return_object.takeError());
278 expected_return_object = init.Call(args...);
279 },
280 transformed_args);
281 }
282
283 if (!expected_return_object)
284 // Drain the Python exception into a plain string while the GIL is
285 // still held: `PythonException` owns raw `PyObject*` references, and
286 // `py_lock` (and the GIL it holds) is released as this function
287 // returns, before the caller gets a chance to touch the error.
288 return llvm::createStringError(
289 ExtractPythonError(expected_return_object.takeError()));
290 result = expected_return_object.get();
291 }
292
293 if (!result.IsValid())
294 return create_error("Resulting object is not a valid Python Object.");
295 if (!result.HasAttribute("__class__"))
296 return create_error("Resulting object doesn't have '__class__' member.");
297
298 PythonObject obj_class = result.GetAttributeValue("__class__");
299 if (!obj_class.IsValid())
300 return create_error("Resulting class object is not a valid.");
301 if (!obj_class.HasAttribute("__name__"))
302 return create_error(
303 "Resulting object class doesn't have '__name__' member.");
304 PythonString obj_class_name =
305 obj_class.GetAttributeValue("__name__").AsType<PythonString>();
306
307 auto checker_or_err = CheckAbstractMethodImplementation(obj_class);
308 if (!checker_or_err)
309 return checker_or_err.takeError();
310
311 llvm::Error abstract_method_errors = llvm::Error::success();
312 for (const auto &method_checker : *checker_or_err)
313 switch (method_checker.second.checker_case) {
315 abstract_method_errors = llvm::joinErrors(
316 std::move(abstract_method_errors),
317 std::move(create_error("Abstract method {0}.{1} not implemented.",
318 obj_class_name.GetString(),
319 method_checker.first)));
320 break;
322 abstract_method_errors = llvm::joinErrors(
323 std::move(abstract_method_errors),
324 std::move(create_error("Abstract method {0}.{1} not allocated.",
325 obj_class_name.GetString(),
326 method_checker.first)));
327 break;
329 abstract_method_errors = llvm::joinErrors(
330 std::move(abstract_method_errors),
331 std::move(create_error("Abstract method {0}.{1} not callable.",
332 obj_class_name.GetString(),
333 method_checker.first)));
334 break;
336 const std::string *py_error =
337 std::get_if<std::string>(&method_checker.second.payload);
338 abstract_method_errors = llvm::joinErrors(
339 std::move(abstract_method_errors),
340 std::move(create_error(
341 "abstract method {0}.{1} has unknown argument count: {2}",
342 obj_class_name.GetString(), method_checker.first,
343 py_error ? *py_error : "<no further information>")));
344 } break;
346 auto &payload_variant = method_checker.second.payload;
347 if (!std::holds_alternative<
349 payload_variant)) {
350 abstract_method_errors = llvm::joinErrors(
351 std::move(abstract_method_errors),
352 std::move(create_error(
353 "Abstract method {0}.{1} has unexpected argument count.",
354 obj_class_name.GetString(), method_checker.first)));
355 } else {
356 auto payload = std::get<
358 payload_variant);
359 abstract_method_errors = llvm::joinErrors(
360 std::move(abstract_method_errors),
361 std::move(
362 create_error("Abstract method {0}.{1} has unexpected "
363 "argument count (expected {2} but has {3}).",
364 obj_class_name.GetString(), method_checker.first,
365 payload.required_argument_count,
366 payload.actual_argument_count)));
367 }
368 } break;
370 LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",
371 obj_class_name.GetString(), method_checker.first);
372 break;
373 }
374
375 if (abstract_method_errors) {
376 Status error = Status::FromError(std::move(abstract_method_errors));
377 LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name,
378 error.AsCString());
379 return error.ToError();
380 }
381
383 new StructuredPythonObject(std::move(result)));
385 }
386
387 /// Call a static method on a Python class without creating an instance.
388 ///
389 /// This method resolves a Python class by name and calls a static method
390 /// on it, returning the result. This is useful for calling class-level
391 /// methods that don't require an instance.
392 ///
393 /// \param class_name The fully-qualified name of the Python class.
394 /// \param method_name The name of the static method to call.
395 /// \param error Output parameter to receive error information if the call
396 /// fails.
397 /// \param args Arguments to pass to the static method.
398 ///
399 /// \return The return value of the static method call, or an error value.
400 template <typename T = StructuredData::ObjectSP, typename... Args>
401 T CallStaticMethod(llvm::StringRef class_name, llvm::StringRef method_name,
402 Status &error, Args &&...args) {
403 using namespace python;
405
406 std::string caller_signature =
407 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
408 llvm::Twine(class_name) + llvm::Twine(".") +
409 llvm::Twine(method_name) + llvm::Twine(")"))
410 .str();
411
412 if (class_name.empty())
413 return ErrorWithMessage<T>(caller_signature, "missing script class name",
414 error);
415
418
419 // Get the interpreter dictionary.
420 auto dict =
421 PythonModule::MainModule().ResolveName<python::PythonDictionary>(
422 m_interpreter.GetDictionaryName());
423 if (!dict.IsAllocated())
424 return ErrorWithMessage<T>(
425 caller_signature,
426 llvm::formatv("could not find interpreter dictionary: {0}",
427 m_interpreter.GetDictionaryName())
428 .str(),
429 error);
430
431 // Resolve the class.
432 auto class_obj =
433 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
434 class_name, dict);
435 if (!class_obj.IsAllocated())
436 return ErrorWithMessage<T>(
437 caller_signature,
438 llvm::formatv("could not find script class: {0}", class_name).str(),
439 error);
440
441 // Get the static method from the class.
442 if (!class_obj.HasAttribute(method_name))
443 return ErrorWithMessage<T>(
444 caller_signature,
445 llvm::formatv("class {0} does not have method {1}", class_name,
446 method_name)
447 .str(),
448 error);
449
450 PythonCallable method =
451 class_obj.GetAttributeValue(method_name).AsType<PythonCallable>();
452 if (!method.IsAllocated())
453 return ErrorWithMessage<T>(caller_signature,
454 llvm::formatv("method {0}.{1} is not callable",
455 class_name, method_name)
456 .str(),
457 error);
458
459 // Transform the arguments.
460 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
461 auto transformed_args = TransformArgs(original_args);
462
463 // Call the static method.
464 llvm::Expected<PythonObject> expected_return_object =
465 llvm::createStringError("not initialized");
466 std::apply(
467 [&method, &expected_return_object](auto &&...args) {
468 if (!expected_return_object)
469 llvm::consumeError(expected_return_object.takeError());
470 expected_return_object = method.Call(args...);
471 },
472 transformed_args);
473
474 if (llvm::Error e = expected_return_object.takeError()) {
475 // TODO: Stringify `args` and include them in the message so users
476 // can see what was passed to the failing call (e.g.
477 // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
478 // helper that falls back to a placeholder for types without a
479 // format_provider / operator<<.
480 error = Status::FromErrorString(ExtractPythonError(std::move(e)).c_str());
481
482 return ErrorWithMessage<T>(
483 caller_signature,
484 llvm::formatv("python exception in {0} method '{1}'", class_name,
485 method_name)
486 .str(),
487 error);
488 }
489
490 PythonObject py_return = std::move(expected_return_object.get());
491
492 // Re-assign reference and pointer arguments if needed.
493 if (sizeof...(Args) > 0)
494 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
495 return ErrorWithMessage<T>(
496 caller_signature,
497 "couldn't re-assign reference and pointer arguments", error);
498
499 // Extract value from Python object (handles unallocated case).
500 return ExtractValueFromPythonObject<T>(py_return, error);
501 }
502
503protected:
504 /// Extract detailed error message including Python backtrace if available.
505 ///
506 /// This helper processes llvm::Error objects that may contain PythonException
507 /// instances, extracting full Python backtraces when available.
508 ///
509 /// \param error The llvm::Error to extract information from.
510 /// \return A string containing the error message, including full Python
511 /// backtrace if the error was a PythonException.
512 static std::string ExtractPythonError(llvm::Error error) {
513 std::string error_msg;
514 llvm::handleAllErrors(
515 std::move(error),
516 [&](python::PythonException &E) { error_msg = E.ReadBacktrace(); },
517 [&](const llvm::ErrorInfoBase &E) { error_msg = E.message(); });
518 return error_msg;
519 }
520
521 template <typename T = StructuredData::ObjectSP>
525
526 template <typename T = StructuredData::ObjectSP, typename... Args>
527 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {
528 using namespace python;
530
531 std::string caller_signature =
532 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
533 llvm::Twine(method_name) + llvm::Twine(")"))
534 .str();
536 return ErrorWithMessage<T>(caller_signature, "python object ill-formed",
537 error);
538
541
542 PythonObject implementor(PyRefType::Borrowed,
543 (PyObject *)m_object_instance_sp->GetValue());
544
545 if (!implementor.IsAllocated())
546 return llvm::is_contained(GetAbstractMethods(), method_name)
547 ? ErrorWithMessage<T>(caller_signature,
548 "python implementor not allocated",
549 error)
550 : T{};
551
552 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
553 auto transformed_args = TransformArgs(original_args);
554
555 // Trim trailing args if the Python method accepts fewer positional
556 // parameters than we're passing (e.g. `num_children(self)` vs.
557 // `num_children(self, max_count)`).
558 size_t call_arity = sizeof...(Args);
559 if (PythonObject py_method = implementor.GetAttributeValue(method_name);
560 py_method.IsAllocated()) {
561 PythonCallable callable = py_method.AsType<PythonCallable>();
562 if (callable.IsAllocated()) {
563 if (llvm::Expected<PythonCallable::ArgInfo> arg_info =
564 callable.GetArgInfo()) {
565 if (arg_info->max_positional_args !=
566 PythonCallable::ArgInfo::UNBOUNDED &&
567 arg_info->max_positional_args < call_arity)
568 call_arity = arg_info->max_positional_args;
569 } else {
570 llvm::consumeError(arg_info.takeError());
571 }
572 }
573 }
574
575 llvm::Expected<PythonObject> expected_return_object =
576 llvm::createStringError("not initialized");
577 CallWithArity(call_arity, transformed_args,
578 std::make_index_sequence<sizeof...(Args) + 1>{},
579 [&implementor, &method_name,
580 &expected_return_object](auto &&...call_args) {
581 if (!expected_return_object)
582 llvm::consumeError(expected_return_object.takeError());
583 expected_return_object = implementor.CallMethod(
584 method_name.data(), call_args...);
585 });
586
587 if (llvm::Error e = expected_return_object.takeError()) {
588 // TODO: Stringify `args` and include them in the message so users
589 // can see what was passed to the failing call (e.g.
590 // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
591 // helper that falls back to a placeholder for types without a
592 // format_provider / operator<<.
593 error = Status::FromErrorString(ExtractPythonError(std::move(e)).c_str());
594
595 return ErrorWithMessage<T>(
596 caller_signature,
597 llvm::formatv("python exception in {0} method '{1}'",
599 ? GetScriptedMetadata()->GetClassName()
600 : "<unknown>",
601 method_name)
602 .str(),
603 error);
604 }
605
606 PythonObject py_return = std::move(expected_return_object.get());
607
608 // Now that we called the python method with the transformed arguments,
609 // we need to iterate again over both the original and transformed
610 // parameter pack, and transform back the parameter that were passed in
611 // the original parameter pack as references or pointers.
612 if (sizeof...(Args) > 0)
613 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
614 return ErrorWithMessage<T>(
615 caller_signature,
616 "couldn't re-assign reference and pointer arguments", error);
617
618 if (!py_return.IsAllocated())
619 return {};
620 return ExtractValueFromPythonObject<T>(py_return, error);
621 }
622
623 template <typename... Args>
624 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
626 Dispatch<Status>(method_name, error, std::forward<Args>(args)...);
627
628 return error;
629 }
630
631 template <typename T> T Transform(T object) {
632 // No Transformation for generic usage
633 return {object};
634 }
635
637 // Boolean arguments need to be turned into python objects.
638 return python::PythonBoolean(arg);
639 }
640
644
646 return python::SWIGBridge::ToSWIGWrapper(std::move(arg));
647 }
648
652
653 template <typename T, typename = std::enable_if_t<
654 std::is_base_of_v<StructuredData::Object, T>>>
655 python::PythonObject Transform(std::shared_ptr<T> arg) {
656 return Transform(StructuredDataImpl(arg));
657 }
658
662
666
670
674
678
682
686
690
694
698
702
706
710
714
718
722
726
730
731 python::PythonObject Transform(const std::vector<std::string> &arg) {
733 for (const std::string &s : arg)
735 return list;
736 }
737
742
746
747 template <typename T, typename U>
748 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
749 // If U is not a PythonObject, don't touch it!
750 }
751
752 template <typename T>
753 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
754 Status &error) {
755 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
756 }
757
758 // Read-only arguments (passed as `const T&`) have nothing to write back:
759 // there's no `T` value to reassign into a const reference, and no
760 // `ExtractValueFromPythonObject<T>` specialization should be required just
761 // to satisfy this round-trip for a value the callee never mutates.
762 template <typename T>
763 void ReverseTransform(const T &original_arg,
764 python::PythonObject transformed_arg, Status &error) {}
765
766 void ReverseTransform(bool &original_arg,
767 python::PythonObject transformed_arg, Status &error) {
769 python::PyRefType::Borrowed, transformed_arg.get());
770 if (boolean_arg.IsValid())
771 original_arg = boolean_arg.GetValue();
772 else
774 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION);
775 }
776
777 template <std::size_t... I, typename... Args>
778 auto TransformTuple(const std::tuple<Args...> &args,
779 std::index_sequence<I...>) {
780 return std::make_tuple(Transform(std::get<I>(args))...);
781 }
782
783 // This will iterate over the Dispatch parameter pack and replace in-place
784 // every `lldb_private` argument that has a SB counterpart.
785 template <typename... Args>
786 auto TransformArgs(const std::tuple<Args...> &args) {
787 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
788 }
789
790 // Apply `fn` with the first `N` elements of `t`, for compile-time `N`.
791 template <std::size_t N, typename Tuple, typename Fn, std::size_t... I>
792 static void ApplyPrefixImpl(Tuple &&t, Fn &&fn, std::index_sequence<I...>) {
793 std::forward<Fn>(fn)(std::get<I>(std::forward<Tuple>(t))...);
794 }
795
796 template <std::size_t N, typename Tuple, typename Fn>
797 static void ApplyPrefix(Tuple &&t, Fn &&fn) {
798 ApplyPrefixImpl<N>(std::forward<Tuple>(t), std::forward<Fn>(fn),
799 std::make_index_sequence<N>{});
800 }
801
802 // Call `fn` with a runtime-selected prefix of `t`: exactly `call_arity`
803 // leading elements. `Is...` enumerates every compile-time count in
804 // `[0, sizeof...(Args)]`; the runtime check picks the matching one.
805 template <typename Tuple, std::size_t... Is, typename Fn>
806 static void CallWithArity(size_t call_arity, Tuple &&t,
807 std::index_sequence<Is...>, Fn &&fn) {
808 (void)std::initializer_list<int>{(
809 Is == call_arity
810 ? (ApplyPrefix<Is>(std::forward<Tuple>(t), std::forward<Fn>(fn)), 0)
811 : 0)...};
812 }
813
814 template <typename T, typename U>
815 void TransformBack(T &original_arg, U transformed_arg, Status &error) {
816 ReverseTransform(original_arg, transformed_arg, error);
817 }
818
819 // ScopedPythonObject is non-copyable — passing it through the generic
820 // TransformBack would trigger the deleted copy ctor. It manages its own
821 // cleanup via the destructor when the transformed-args tuple destructs, so
822 // there is nothing to reverse-transform back into the original arg.
823 template <typename T, typename SB>
824 void TransformBack(T &original_arg,
825 python::ScopedPythonObject<SB> &transformed_arg,
826 Status &error) {}
827
828 template <std::size_t... I, typename... Ts, typename... Us>
829 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
830 std::tuple<Us...> &transformed_args,
831 std::index_sequence<I...>) {
833 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
834 error),
835 ...);
836 return error.Success();
837 }
838
839 template <typename... Ts, typename... Us>
840 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
841 std::tuple<Us...> &transformed_args) {
842 if (sizeof...(Ts) != sizeof...(Us))
843 return false;
844
845 return ReassignPtrsOrRefsArgs(original_args, transformed_args,
846 std::make_index_sequence<sizeof...(Ts)>());
847 }
848
849 template <typename T, typename... Args>
850 void FormatArgs(std::string &fmt, T arg, Args... args) const {
851 FormatArgs(fmt, arg);
852 FormatArgs(fmt, args...);
853 }
854
855 template <typename T> void FormatArgs(std::string &fmt, T arg) const {
857 }
858
859 void FormatArgs(std::string &fmt) const {}
860
861 // The lifetime is managed by the ScriptInterpreter
863};
864
865template <>
869
870template <>
874
875template <>
878
879template <>
882
883template <>
887
888template <>
892
893template <>
897
898template <>
902
903template <>
907
908template <>
912
913template <>
916
917template <>
920
921template <>
925
926template <>
927std::optional<MemoryRegionInfo>
929 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);
930
931template <>
935
936template <>
940
941template <>
945
946template <>
950
951template <>
955
956template <>
960
961template <>
962std::optional<lldb::ValueType>
964 std::optional<lldb::ValueType>>(python::PythonObject &p, Status &error);
965
966template <>
970
971template <>
972std::vector<std::string>
975
976} // namespace lldb_private
977
978#endif // LLDB_SOURCE_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:375
ScriptInterpreterPythonImpl::Locker Locker
#define SET_CASE_AND_CONTINUE(method_name, case)
A command line argument class.
Definition Args.h:33
A file utility class.
Definition FileSpec.h:56
static Ret ErrorWithMessage(llvm::StringRef caller_name, llvm::StringRef user_msg, Status &error, LLDBLog log_category=LLDBLog::Process)
std::optional< ScriptedMetadata > m_scripted_metadata
llvm::SmallVector< llvm::StringLiteral > const GetAbstractMethods() const
virtual llvm::SmallVector< AbstractMethodRequirement > GetAbstractMethodRequirements() const =0
const std::optional< ScriptedMetadata > & GetScriptedMetadata() const
StructuredData::GenericSP m_object_instance_sp
llvm::StringRef GetClassName() const
python::ScopedPythonObject< lldb::SBCommandReturnObject > Transform(CommandReturnObject *arg)
python::PythonObject Transform(lldb::StackFrameListSP arg)
python::PythonObject Transform(lldb::ThreadPlanSP arg)
Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args)
python::PythonObject Transform(lldb::ProcessSP arg)
static void ApplyPrefix(Tuple &&t, Fn &&fn)
void TransformBack(T &original_arg, U transformed_arg, Status &error)
ScriptInterpreterPythonImpl & m_interpreter
python::PythonObject Transform(Event *arg)
auto TransformArgs(const std::tuple< Args... > &args)
python::PythonObject Transform(lldb::ExecutionContextRefSP arg)
python::PythonObject Transform(lldb::ThreadSP arg)
python::PythonObject Transform(const StructuredDataImpl &arg)
T ExtractValueFromPythonObject(python::PythonObject &p, Status &error)
~ScriptedPythonInterface() override=default
static void CallWithArity(size_t call_arity, Tuple &&t, std::index_sequence< Is... >, Fn &&fn)
python::PythonObject Transform(const TypeSummaryOptions &arg)
llvm::Expected< FileSpec > GetScriptedModulePath() override
python::PythonObject Transform(lldb::DescriptionLevel arg)
python::PythonObject Transform(const Status &arg)
python::PythonObject Transform(const SymbolContext &arg)
llvm::Expected< std::map< llvm::StringLiteral, AbstractMethodCheckerPayload > > CheckAbstractMethodImplementation(const python::PythonObject &obj_class) const
void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, Status &error)
void FormatArgs(std::string &fmt, T arg, Args... args) const
python::PythonObject Transform(lldb::BreakpointSP arg)
void FormatArgs(std::string &fmt, T arg) const
python::PythonObject Transform(lldb::DebuggerSP arg)
static std::string ExtractPythonError(llvm::Error error)
Extract detailed error message including Python backtrace if available.
ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter)
python::PythonObject Transform(const std::vector< std::string > &arg)
void ReverseTransform(T &original_arg, U transformed_arg, Status &error)
T CallStaticMethod(llvm::StringRef class_name, llvm::StringRef method_name, Status &error, Args &&...args)
Call a static method on a Python class without creating an instance.
T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args)
python::PythonObject Transform(lldb::BreakpointLocationSP arg)
python::PythonObject Transform(std::shared_ptr< T > arg)
bool ReassignPtrsOrRefsArgs(std::tuple< Ts... > &original_args, std::tuple< Us... > &transformed_args)
python::PythonObject Transform(lldb::StreamSP arg)
python::PythonObject Transform(Status &&arg)
python::PythonObject Transform(lldb::DataExtractorSP arg)
python::PythonObject Transform(lldb::StackFrameSP arg)
bool ReassignPtrsOrRefsArgs(std::tuple< Ts... > &original_args, std::tuple< Us... > &transformed_args, std::index_sequence< I... >)
python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg)
llvm::Expected< StructuredData::GenericSP > CreatePluginObject(const ScriptedMetadata &scripted_metadata, StructuredData::Generic *script_obj, Args... args)
python::PythonObject Transform(lldb::TargetSP arg)
void TransformBack(T &original_arg, python::ScopedPythonObject< SB > &transformed_arg, Status &error)
auto TransformTuple(const std::tuple< Args... > &args, std::index_sequence< I... >)
static void ApplyPrefixImpl(Tuple &&t, Fn &&fn, std::index_sequence< I... >)
void ReverseTransform(bool &original_arg, python::PythonObject transformed_arg, Status &error)
python::PythonObject Transform(lldb::ValueObjectSP arg)
void ReverseTransform(const T &original_arg, python::PythonObject transformed_arg, Status &error)
python::PythonObject Transform(lldb::ProcessAttachInfoSP arg)
An error handling class.
Definition Status.h:118
Status Clone() const
Don't call this function in new code.
Definition Status.h:174
static Status FromErrorString(const char *str)
Definition Status.h:141
static Status static Status FromErrorStringWithFormatv(const char *format, Args &&...args)
Definition Status.h:151
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:136
std::shared_ptr< Generic > GenericSP
std::shared_ptr< Dictionary > DictionarySP
std::shared_ptr< Object > ObjectSP
std::shared_ptr< Array > ArraySP
Defines a symbol context baton that can be handed other debug core functions.
void AppendItem(const PythonObject &object)
StructuredData::ObjectSP CreateStructuredObject() const
PythonObject GetAttributeValue(llvm::StringRef attribute) const
bool HasAttribute(llvm::StringRef attribute) const
static PythonObject ToSWIGWrapper(std::unique_ptr< lldb::SBValue > value_sb)
A class that automatically clears an SB object when it goes out of scope.
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:338
std::shared_ptr< lldb_private::ThreadPlan > ThreadPlanSP
std::shared_ptr< lldb_private::StackFrame > StackFrameSP
std::shared_ptr< lldb_private::BreakpointLocation > BreakpointLocationSP
DescriptionLevel
Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls.
std::shared_ptr< lldb_private::Thread > ThreadSP
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
std::shared_ptr< lldb_private::ProcessAttachInfo > ProcessAttachInfoSP
std::shared_ptr< lldb_private::Stream > StreamSP
std::shared_ptr< lldb_private::Breakpoint > BreakpointSP
std::shared_ptr< lldb_private::Process > ProcessSP
std::shared_ptr< lldb_private::ValueObjectList > ValueObjectListSP
std::shared_ptr< lldb_private::Debugger > DebuggerSP
std::shared_ptr< lldb_private::Target > TargetSP
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
std::shared_ptr< lldb_private::ProcessLaunchInfo > ProcessLaunchInfoSP
std::shared_ptr< lldb_private::StackFrameList > StackFrameListSP
std::shared_ptr< lldb_private::ExecutionContextRef > ExecutionContextRefSP
std::variant< std::monostate, InvalidArgumentCountPayload, std::string > payload