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#include "lldb/Utility/Policy.h"
22
24#include "../SWIGPythonBridge.h"
26
27namespace lldb_private {
30public:
32 ~ScriptedPythonInterface() override = default;
33
42
44
52
54 std::variant<std::monostate, InvalidArgumentCountPayload, std::string>
56 };
57
58 llvm::Expected<FileSpec> GetScriptedModulePath() override {
59 using namespace python;
61
64
66 return llvm::createStringError("scripted Interface has invalid object");
67
68 PythonObject py_obj =
69 PythonObject(PyRefType::Borrowed,
70 static_cast<PyObject *>(m_object_instance_sp->GetValue()));
71
72 if (!py_obj.IsAllocated())
73 return llvm::createStringError(
74 "scripted Interface has invalid python object");
75
76 PythonObject py_obj_class = py_obj.GetAttributeValue("__class__");
77 if (!py_obj_class.IsValid())
78 return llvm::createStringError(
79 "scripted Interface python object is missing '__class__' attribute");
80
81 PythonObject py_obj_module = py_obj_class.GetAttributeValue("__module__");
82 if (!py_obj_module.IsValid())
83 return llvm::createStringError(
84 "scripted Interface python object '__class__' is missing "
85 "'__module__' attribute");
86
87 PythonString py_obj_module_str = py_obj_module.Str();
88 if (!py_obj_module_str.IsValid())
89 return llvm::createStringError(
90 "scripted Interface python object '__class__.__module__' attribute "
91 "is not a string");
92
93 return GetModuleFile(py_obj_module_str.GetString());
94 }
95
96 /// The file the already imported \p module_name was loaded from, read from
97 /// its `__file__`. The caller must hold the GIL.
98 static llvm::Expected<FileSpec> GetModuleFile(llvm::StringRef module_name) {
99 using namespace python;
100
101 PythonModule py_module = PythonModule::AddModule(module_name);
102 if (!py_module.IsValid())
103 return llvm::createStringError(
104 llvm::formatv("failed to import '{0}' module", module_name).str());
105
106 PythonObject py_module_file = py_module.GetAttributeValue("__file__");
107 if (!py_module_file.IsValid())
108 return llvm::createStringError(
109 llvm::formatv("module '{0}' is missing '__file__' attribute",
110 module_name)
111 .str());
112
113 PythonString py_module_file_str = py_module_file.Str();
114 if (!py_module_file_str.IsValid())
115 return llvm::createStringError(
116 llvm::formatv("module '{0}.__file__' attribute is not a string",
117 module_name)
118 .str());
119
120 return FileSpec(py_module_file_str.GetString());
121 }
122
123 llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerPayload>>
125 const python::PythonObject &obj_class) const {
126
127 using namespace python;
128
129 std::map<llvm::StringLiteral, AbstractMethodCheckerPayload> checker;
130#define SET_CASE_AND_CONTINUE(method_name, case) \
131 { \
132 checker[method_name] = {case, {}}; \
133 continue; \
134 }
135
136 for (const AbstractMethodRequirement &requirement :
138 llvm::StringLiteral method_name = requirement.name;
139 // Look up via attribute access so inherited methods are found; the
140 // class's own __dict__ omits anything defined on a base class.
141 if (!obj_class.HasAttribute(method_name))
142 SET_CASE_AND_CONTINUE(method_name,
144 PythonObject attr = obj_class.GetAttributeValue(method_name);
145 if (!attr.IsAllocated())
146 SET_CASE_AND_CONTINUE(method_name,
148
149 PythonCallable callable = attr.AsType<PythonCallable>();
150 if (!callable)
151 SET_CASE_AND_CONTINUE(method_name,
153
154 if (!requirement.min_arg_count)
156
157 auto arg_info_or_err = callable.GetArgInfo();
158 if (!arg_info_or_err) {
159 checker[method_name] = {
161 ExtractPythonError(arg_info_or_err.takeError())};
162 continue;
163 }
164
165 PythonCallable::ArgInfo arg_info = *arg_info_or_err;
166 if (requirement.min_arg_count <= arg_info.max_positional_args) {
168 } else {
169 checker[method_name] = {
172 requirement.min_arg_count, arg_info.max_positional_args)};
173 }
174 }
175
176#undef SET_CASE_AND_CONTINUE
177
178 return checker;
179 }
180
181 /// Diagnose every abstract-method violation on \a obj_class at once.
182 ///
183 /// \a obj_class is a class object, whether resolved by name or taken from an
184 /// instance's `__class__`. Resolving it by name means this can run before any
185 /// instance exists (see CreatePluginObject), which uses it to reject a
186 /// malformed class without executing its `__init__`.
187 llvm::Error CheckAbstractMethods(const python::PythonObject &obj_class,
188 llvm::StringRef qualified_class_name) const {
189 Log *log = GetLog(LLDBLog::Script);
190
191 // Per-method diagnostics name the class the way Python does, unqualified.
192 python::PythonString obj_class_name =
193 obj_class.GetAttributeValue("__name__").AsType<python::PythonString>();
194 llvm::StringRef class_name = obj_class_name.IsValid()
195 ? obj_class_name.GetString()
196 : qualified_class_name;
197 auto create_error = [](llvm::StringLiteral format, auto &&...ts) {
198 return llvm::createStringError(
199 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)
200 .str());
201 };
202
203 auto checker_or_err = CheckAbstractMethodImplementation(obj_class);
204 if (!checker_or_err)
205 return checker_or_err.takeError();
206
207 llvm::Error abstract_method_errors = llvm::Error::success();
208 for (const auto &method_checker : *checker_or_err)
209 switch (method_checker.second.checker_case) {
211 abstract_method_errors = llvm::joinErrors(
212 std::move(abstract_method_errors),
213 create_error("abstract method {0}.{1} not implemented", class_name,
214 method_checker.first));
215 break;
217 abstract_method_errors = llvm::joinErrors(
218 std::move(abstract_method_errors),
219 create_error("abstract method {0}.{1} not allocated", class_name,
220 method_checker.first));
221 break;
223 abstract_method_errors = llvm::joinErrors(
224 std::move(abstract_method_errors),
225 create_error("abstract method {0}.{1} not callable", class_name,
226 method_checker.first));
227 break;
229 const std::string *py_error =
230 std::get_if<std::string>(&method_checker.second.payload);
231 abstract_method_errors = llvm::joinErrors(
232 std::move(abstract_method_errors),
233 create_error(
234 "abstract method {0}.{1} has unknown argument count: {2}",
235 class_name, method_checker.first,
236 py_error ? *py_error : "<no further information>"));
237 } break;
239 auto &payload_variant = method_checker.second.payload;
240 if (!std::holds_alternative<
242 payload_variant)) {
243 abstract_method_errors = llvm::joinErrors(
244 std::move(abstract_method_errors),
245 create_error(
246 "abstract method {0}.{1} has unexpected argument count",
247 class_name, method_checker.first));
248 } else {
249 auto payload = std::get<
251 payload_variant);
252 abstract_method_errors = llvm::joinErrors(
253 std::move(abstract_method_errors),
254 create_error("abstract method {0}.{1} has unexpected "
255 "argument count (expected {2} but has {3})",
256 class_name, method_checker.first,
257 payload.required_argument_count,
258 payload.actual_argument_count));
259 }
260 } break;
262 LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",
263 class_name, method_checker.first);
264 break;
265 }
266
267 if (abstract_method_errors) {
268 Status error = Status::FromError(std::move(abstract_method_errors));
269 LLDB_LOG(log, "Abstract method error in {0}:\n{1}", qualified_class_name,
270 error.AsCString());
271 return error.ToError();
272 }
273
274 return llvm::Error::success();
275 }
276
277 template <typename... Args>
278 llvm::Expected<StructuredData::GenericSP>
279 CreatePluginObject(const ScriptedMetadata &scripted_metadata,
280 StructuredData::Generic *script_obj, Args... args) {
281 using namespace python;
283
284 auto create_error = [](llvm::StringLiteral format, auto &&...ts) {
285 return llvm::createStringError(
286 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)
287 .str());
288 };
289
290 m_scripted_metadata = scripted_metadata;
291 llvm::StringRef class_name = scripted_metadata.GetClassName();
292 bool has_class_name = !class_name.empty();
293 bool has_interpreter_dict =
294 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());
295 if (!has_class_name && !has_interpreter_dict && !script_obj) {
296 if (!has_class_name)
297 return create_error("missing script class name");
298 else if (!has_interpreter_dict)
299 return create_error("invalid script interpreter dictionary");
300 else
301 return create_error("missing scripting object");
302 }
303
304 std::optional<PolicyStack::Guard> policy_guard;
305 if (!UserCanRunDirectly())
307
310
311 PythonObject result = {};
312
313 if (script_obj) {
314 result = PythonObject(PyRefType::Borrowed,
315 static_cast<PyObject *>(script_obj->GetValue()));
316 } else {
317 auto dict =
318 PythonModule::MainModule().ResolveName<python::PythonDictionary>(
319 m_interpreter.GetDictionaryName());
320 if (!dict.IsAllocated())
321 return create_error("could not find interpreter dictionary: {0}",
322 m_interpreter.GetDictionaryName());
323
324 auto init =
325 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
326 class_name, dict);
327 if (!init.IsAllocated())
328 return create_error("could not find script class: {0}",
329 class_name.data());
330
331 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
332 auto transformed_args = TransformArgs(original_args);
333
334 std::string error_string;
335 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();
336 if (!arg_info) {
337 llvm::handleAllErrors(
338 arg_info.takeError(),
339 [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
340 [&](const llvm::ErrorInfoBase &E) {
341 error_string.append(E.message());
342 });
343 return llvm::createStringError(llvm::inconvertibleErrorCode(),
344 error_string);
345 }
346
347 if (llvm::Error error = CheckAbstractMethods(init, class_name))
348 return std::move(error);
349
350 llvm::Expected<PythonObject> expected_return_object =
351 create_error("resulting object is not initialized");
352
353 // This relax the requirement on the number of argument for
354 // initializing scripting extension if the size of the interface
355 // parameter pack contains 1 less element than the extension maximum
356 // number of positional arguments for this initializer.
357 //
358 // This addresses the cases where the embedded interpreter session
359 // dictionary is passed to the extension initializer which is not used
360 // most of the time.
361 // Note, though none of our API's suggest defining the interfaces with
362 // varargs, we have some extant clients that were doing that. To keep
363 // from breaking them, we just say putting a varargs in these signatures
364 // turns off argument checking.
365 size_t num_args = sizeof...(Args);
366 if (arg_info->max_positional_args != PythonCallable::ArgInfo::UNBOUNDED &&
367 num_args != arg_info->max_positional_args) {
368 if (num_args != arg_info->max_positional_args - 1) {
369 // `expected_return_object` starts in an error state; consume it
370 // before we return with a different error, or its destructor
371 // will abort.
372 llvm::consumeError(expected_return_object.takeError());
373 return create_error("passed arguments ({0}) doesn't match the number "
374 "of expected arguments ({1})",
375 num_args, arg_info->max_positional_args);
376 }
377
378 std::apply(
379 [&init, &expected_return_object](auto &&...args) {
380 if (!expected_return_object)
381 llvm::consumeError(expected_return_object.takeError());
382 expected_return_object = init.Call(args...);
383 },
384 std::tuple_cat(transformed_args, std::make_tuple(dict)));
385 } else {
386 std::apply(
387 [&init, &expected_return_object](auto &&...args) {
388 if (!expected_return_object)
389 llvm::consumeError(expected_return_object.takeError());
390 expected_return_object = init.Call(args...);
391 },
392 transformed_args);
393 }
394
395 if (!expected_return_object)
396 // Drain the Python exception into a plain string while the GIL is
397 // still held: `PythonException` owns raw `PyObject*` references, and
398 // `py_lock` (and the GIL it holds) is released as this function
399 // returns, before the caller gets a chance to touch the error.
400 return llvm::createStringError(
401 ExtractPythonError(expected_return_object.takeError()));
402 result = expected_return_object.get();
403 }
404
405 if (!result.IsValid())
406 return create_error("resulting object is not a valid Python Object");
407 if (!result.HasAttribute("__class__"))
408 return create_error("resulting object doesn't have '__class__' member");
409
410 PythonObject obj_class = result.GetAttributeValue("__class__");
411 if (!obj_class.IsValid())
412 return create_error("resulting class object is not a valid");
413 if (!obj_class.HasAttribute("__name__"))
414 return create_error(
415 "resulting object class doesn't have '__name__' member");
416 PythonString obj_class_name =
417 obj_class.GetAttributeValue("__name__").AsType<PythonString>();
418
419 // We were handed an instance rather than building one, so there was no
420 // constructor to run the check ahead of; validate it now.
421 if (script_obj)
422 if (llvm::Error error =
423 CheckAbstractMethods(obj_class, obj_class_name.GetString()))
424 return std::move(error);
425
427 new StructuredPythonObject(std::move(result)));
428
429 // An object handed over by the script wasn't built from the metadata,
430 // which may even describe its creator, so name it after its own class and
431 // don't attribute the metadata's arguments to it.
432 std::string qualified_class_name = script_obj ? "" : class_name.str();
433 PythonString obj_module_name =
434 obj_class.GetAttributeValue("__module__").AsType<PythonString>();
435 if (qualified_class_name.empty() && obj_module_name.IsValid())
436 qualified_class_name =
437 llvm::formatv("{0}.{1}", obj_module_name.GetString(),
438 obj_class_name.GetString())
439 .str();
440
441 // Modules can be imported or reloaded without LLDB seeing it, so ask the
442 // class's module for its current file rather than remembering one.
443 FileSpec source_path;
444 if (obj_module_name.IsValid()) {
445 if (llvm::Expected<FileSpec> path_or_err =
446 GetModuleFile(obj_module_name.GetString()))
447 source_path = *path_or_err;
448 else
449 LLDB_LOG_ERROR(GetLog(LLDBLog::Script), path_or_err.takeError(),
450 "failed to find the file defining {1}: {0}",
451 qualified_class_name);
452 }
453 if (!script_obj)
454 m_scripted_metadata->SetSourcePath(source_path);
455 RegisterInstance(m_interpreter.GetScriptedInstanceRegistry(),
456 std::move(qualified_class_name), std::move(source_path),
457 script_obj ? nullptr : m_scripted_metadata->GetArgsSP());
458
460 }
461
462 /// Call a static method on a Python class without creating an instance.
463 ///
464 /// This method resolves a Python class by name and calls a static method
465 /// on it, returning the result. This is useful for calling class-level
466 /// methods that don't require an instance.
467 ///
468 /// \param class_name The fully-qualified name of the Python class.
469 /// \param method_name The name of the static method to call.
470 /// \param args Arguments to pass to the static method.
471 ///
472 /// \return The return value of the static method call, or an error.
473 template <typename T = StructuredData::ObjectSP, typename... Args>
474 llvm::Expected<T> CallStaticMethod(llvm::StringRef class_name,
475 llvm::StringRef method_name,
476 Args &&...args) {
477 using namespace python;
479
480 std::string caller_signature =
481 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
482 llvm::Twine(class_name) + llvm::Twine(".") +
483 llvm::Twine(method_name) + llvm::Twine(")"))
484 .str();
485
486 if (class_name.empty())
487 return LogAndError(caller_signature, "missing script class name");
488
489 std::optional<PolicyStack::Guard> policy_guard;
490 if (!UserCanRunDirectly())
492
495
496 // Get the interpreter dictionary.
497 auto dict =
498 PythonModule::MainModule().ResolveName<python::PythonDictionary>(
499 m_interpreter.GetDictionaryName());
500 if (!dict.IsAllocated())
501 return LogAndError(caller_signature,
502 "could not find interpreter dictionary: {0}",
503 m_interpreter.GetDictionaryName());
504
505 // Resolve the class.
506 auto class_obj =
507 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
508 class_name, dict);
509 if (!class_obj.IsAllocated())
510 return LogAndError(caller_signature, "could not find script class: {0}",
511 class_name);
512
513 // Get the static method from the class.
514 if (!class_obj.HasAttribute(method_name))
515 return llvm::make_error<UnimplementedError>(
516 llvm::formatv("{0}.{1}", class_name, method_name).str());
517
518 PythonCallable method =
519 class_obj.GetAttributeValue(method_name).AsType<PythonCallable>();
520 if (!method.IsAllocated())
521 return LogAndError(caller_signature, "method {0}.{1} is not callable",
522 class_name, method_name);
523
524 // Transform the arguments.
525 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
526 auto transformed_args = TransformArgs(original_args);
527
528 // Call the static method.
529 llvm::Expected<PythonObject> expected_return_object =
530 llvm::createStringError("not initialized");
531 std::apply(
532 [&method, &expected_return_object](auto &&...args) {
533 if (!expected_return_object)
534 llvm::consumeError(expected_return_object.takeError());
535 expected_return_object = method.Call(args...);
536 },
537 transformed_args);
538
539 if (llvm::Error e = expected_return_object.takeError()) {
540 // TODO: Stringify `args` and include them in the message so users
541 // can see what was passed to the failing call (e.g.
542 // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
543 // helper that falls back to a placeholder for types without a
544 // format_provider / operator<<.
545 return LogAndError(
546 caller_signature, "python exception in {0} method '{1}': {2}",
547 class_name, method_name, ExtractPythonError(std::move(e)));
548 }
549
550 PythonObject py_return = std::move(expected_return_object.get());
551
552 // Re-assign reference and pointer arguments if needed.
553 if (sizeof...(Args) > 0)
554 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
555 return LogAndError(
556 caller_signature,
557 "couldn't re-assign reference and pointer arguments");
558
559 // Extract value from Python object (handles unallocated case).
560 if (!py_return.IsAllocated())
561 return T{};
562 return ExtractValueFromPythonObject<T>(py_return);
563 }
564
565protected:
566 /// Extract detailed error message including Python backtrace if available.
567 ///
568 /// This helper processes llvm::Error objects that may contain PythonException
569 /// instances, extracting full Python backtraces when available.
570 ///
571 /// \param error The llvm::Error to extract information from.
572 /// \return A string containing the error message, including full Python
573 /// backtrace if the error was a PythonException.
574 static std::string ExtractPythonError(llvm::Error error) {
575 std::string error_msg;
576 llvm::handleAllErrors(
577 std::move(error),
578 [&](python::PythonException &E) { error_msg = E.ReadBacktrace(); },
579 [&](const llvm::ErrorInfoBase &E) { error_msg = E.message(); });
580 return error_msg;
581 }
582
583 /// Log \a message against \a caller_name and return it as an error.
584 ///
585 /// The `Expected`-returning counterpart of
586 /// `ScriptedInterface::ErrorWithMessage`: it reports the failure instead of
587 /// folding it into a default-constructed value.
588 template <typename... Ts>
589 static llvm::Error LogAndError(llvm::StringRef caller_name,
590 const char *format, Ts &&...ts) {
591 std::string message = llvm::formatv(format, std::forward<Ts>(ts)...).str();
592 LLDB_LOGF(GetLog(LLDBLog::Script), "%s ERROR = %s", caller_name.data(),
593 message.c_str());
594 return llvm::createStringError(message);
595 }
596
597 /// Log the failure in \a value_or_err and fall back to a default-constructed
598 /// \c T.
599 ///
600 /// For entry points whose return type cannot express failure. Prefer
601 /// propagating the error wherever the signature can carry it; this is the
602 /// stop-gap, and it is at least strictly better than dropping the error.
603 ///
604 /// Logs any failure, an unimplemented method included: a site that dispatches
605 /// with Dispatch() rather than DispatchToOptional() is asserting the method
606 /// should have been there.
607 template <typename T>
608 static T LogAndDefault(llvm::Expected<T> value_or_err,
609 llvm::StringRef caller) {
610 if (value_or_err)
611 return std::move(*value_or_err);
612
613 // Consume the error before logging: LLDB_LOGF doesn't evaluate its
614 // arguments when the channel is disabled, which would leave the error
615 // unchecked and abort.
616 std::string message = llvm::toString(value_or_err.takeError());
617 LLDB_LOGF(GetLog(LLDBLog::Script), "%s failed: %s", caller.str().c_str(),
618 message.c_str());
619 return T{};
620 }
621
622 /// Call an optional \a method_name, reporting "the script doesn't implement
623 /// it" as \c std::nullopt rather than as a failure.
624 ///
625 /// Use this for callbacks a script may legitimately leave out. Any other
626 /// failure - in particular an exception raised inside the method -
627 /// propagates; this never papers over a method that ran and failed.
628 template <typename T = StructuredData::ObjectSP, typename... Args>
629 llvm::Expected<std::optional<T>>
630 DispatchToOptional(llvm::StringRef method_name, Args &&...args) {
631 if (!llvm::is_contained(GetOptionalMethods(), method_name))
632 return LogAndError(
633 LLVM_PRETTY_FUNCTION,
634 "method '{0}' is not declared optional: list it in "
635 "GetOptionalMethods(), or dispatch it with Dispatch(), "
636 "where a missing method is an error",
637 method_name);
638
639 llvm::Expected<T> value_or_err =
640 Dispatch<T>(method_name, std::forward<Args>(args)...);
641 if (value_or_err)
642 return std::move(*value_or_err);
643
644 if (value_or_err.template errorIsA<UnimplementedError>()) {
645 llvm::consumeError(value_or_err.takeError());
646 return std::nullopt;
647 }
648 return value_or_err.takeError();
649 }
650
651 template <typename T = StructuredData::ObjectSP>
653 return p.CreateStructuredObject();
654 }
655
656 /// Call \a method_name on the scripted object.
657 ///
658 /// The returned \c Expected separates the two outcomes that a plain return
659 /// value cannot: a successfully extracted value (which may legitimately be
660 /// empty or null, e.g. when the Python method returns \c None) and a
661 /// failure to call the method at all.
662 ///
663 /// A class that simply doesn't implement \a method_name fails with an
664 /// \c UnimplementedError, so callers of optional callbacks can recognize
665 /// and ignore that case without also swallowing real exceptions.
666 template <typename T = StructuredData::ObjectSP, typename... Args>
667 llvm::Expected<T> Dispatch(llvm::StringRef method_name, Args &&...args) {
668 using namespace python;
670
671 std::string caller_signature =
672 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +
673 llvm::Twine(method_name) + llvm::Twine(")"))
674 .str();
676 return LogAndError(caller_signature, "python object ill-formed");
677
678 std::optional<PolicyStack::Guard> policy_guard;
679 if (!UserCanRunDirectly())
681
684
685 PythonObject implementor(PyRefType::Borrowed,
686 (PyObject *)m_object_instance_sp->GetValue());
687
688 if (!implementor.IsAllocated())
689 return LogAndError(caller_signature, "python implementor not allocated");
690
691 PythonObject py_method = implementor.GetAttributeValue(method_name);
692 if (!py_method.IsAllocated())
693 return llvm::make_error<UnimplementedError>(
694 llvm::formatv("{0}.{1}",
696 ? GetScriptedMetadata()->GetClassName()
697 : "<unknown>",
698 method_name)
699 .str());
700
701 std::tuple<Args...> original_args = std::forward_as_tuple(args...);
702 auto transformed_args = TransformArgs(original_args);
703
704 // Trim trailing args if the Python method accepts fewer positional
705 // parameters than we're passing (e.g. `num_children(self)` vs.
706 // `num_children(self, max_count)`).
707 size_t call_arity = sizeof...(Args);
708 if (PythonCallable callable = py_method.AsType<PythonCallable>();
709 callable.IsAllocated()) {
710 if (llvm::Expected<PythonCallable::ArgInfo> arg_info =
711 callable.GetArgInfo()) {
712 if (arg_info->max_positional_args !=
713 PythonCallable::ArgInfo::UNBOUNDED &&
714 arg_info->max_positional_args < call_arity)
715 call_arity = arg_info->max_positional_args;
716 } else {
717 llvm::consumeError(arg_info.takeError());
718 }
719 }
720
721 llvm::Expected<PythonObject> expected_return_object =
722 llvm::createStringError("not initialized");
723 CallWithArity(call_arity, transformed_args,
724 std::make_index_sequence<sizeof...(Args) + 1>{},
725 [&implementor, &method_name,
726 &expected_return_object](auto &&...call_args) {
727 if (!expected_return_object)
728 llvm::consumeError(expected_return_object.takeError());
729 expected_return_object = implementor.CallMethod(
730 method_name.data(), call_args...);
731 });
732
733 if (llvm::Error e = expected_return_object.takeError()) {
734 // TODO: Stringify `args` and include them in the message so users
735 // can see what was passed to the failing call (e.g.
736 // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
737 // helper that falls back to a placeholder for types without a
738 // format_provider / operator<<.
739 //
740 // Drain the Python exception into a plain string while the GIL is
741 // still held: `PythonException` owns raw `PyObject*` references, and
742 // `py_lock` is released as this function returns.
743 return LogAndError(
744 caller_signature, "python exception in {0} method '{1}': {2}",
745 GetScriptedMetadata() ? GetScriptedMetadata()->GetClassName()
746 : "<unknown>",
747 method_name, ExtractPythonError(std::move(e)));
748 }
749
750 PythonObject py_return = std::move(expected_return_object.get());
751
752 // Now that we called the python method with the transformed arguments,
753 // we need to iterate again over both the original and transformed
754 // parameter pack, and transform back the parameter that were passed in
755 // the original parameter pack as references or pointers.
756 if (sizeof...(Args) > 0)
757 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))
758 return LogAndError(
759 caller_signature,
760 "couldn't re-assign reference and pointer arguments");
761
762 if (!py_return.IsAllocated())
763 return T{};
764 return ExtractValueFromPythonObject<T>(py_return);
765 }
766
767 /// Call \a method_name and fold both failure channels into one `Status`:
768 /// a failure to call the method at all, and the `SBError` the method
769 /// returned. The latter used to be dropped on the floor.
770 template <typename... Args>
771 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {
772 llvm::Expected<Status> status_or_err =
773 Dispatch<Status>(method_name, std::forward<Args>(args)...);
774 if (!status_or_err)
775 return Status::FromError(status_or_err.takeError());
776
777 return std::move(*status_or_err);
778 }
779
780 template <typename T> T Transform(T object) {
781 // No Transformation for generic usage
782 return {object};
783 }
784
786 // Boolean arguments need to be turned into python objects.
787 return python::PythonBoolean(arg);
788 }
789
793
795 return python::SWIGBridge::ToSWIGWrapper(std::move(arg));
796 }
797
801
802 template <typename T, typename = std::enable_if_t<
803 std::is_base_of_v<StructuredData::Object, T>>>
804 python::PythonObject Transform(std::shared_ptr<T> arg) {
805 return Transform(StructuredDataImpl(arg));
806 }
807
811
815
819
823
827
831
835
839
843
847
851
855
859
863
867
871
875
879
883
884 python::PythonObject Transform(const std::vector<std::string> &arg) {
886 for (const std::string &s : arg)
888 return list;
889 }
890
895
899
900 template <typename T, typename U>
901 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
902 // If U is not a PythonObject, don't touch it!
903 }
904
905 template <typename T>
906 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,
907 Status &error) {
908 llvm::Expected<T> value_or_err =
909 ExtractValueFromPythonObject<T>(transformed_arg);
910 if (!value_or_err) {
911 error = Status::FromError(value_or_err.takeError());
912 return;
913 }
914 original_arg = std::move(*value_or_err);
915 }
916
917 // Read-only arguments (passed as `const T&`) have nothing to write back:
918 // there's no `T` value to reassign into a const reference, and no
919 // `ExtractValueFromPythonObject<T>` specialization should be required just
920 // to satisfy this round-trip for a value the callee never mutates.
921 template <typename T>
922 void ReverseTransform(const T &original_arg,
923 python::PythonObject transformed_arg, Status &error) {}
924
925 void ReverseTransform(bool &original_arg,
926 python::PythonObject transformed_arg, Status &error) {
928 python::PyRefType::Borrowed, transformed_arg.get());
929 if (boolean_arg.IsValid())
930 original_arg = boolean_arg.GetValue();
931 else
933 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION);
934 }
935
936 template <std::size_t... I, typename... Args>
937 auto TransformTuple(const std::tuple<Args...> &args,
938 std::index_sequence<I...>) {
939 return std::make_tuple(Transform(std::get<I>(args))...);
940 }
941
942 // This will iterate over the Dispatch parameter pack and replace in-place
943 // every `lldb_private` argument that has a SB counterpart.
944 template <typename... Args>
945 auto TransformArgs(const std::tuple<Args...> &args) {
946 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
947 }
948
949 // Apply `fn` with the first `N` elements of `t`, for compile-time `N`.
950 template <std::size_t N, typename Tuple, typename Fn, std::size_t... I>
951 static void ApplyPrefixImpl(Tuple &&t, Fn &&fn, std::index_sequence<I...>) {
952 std::forward<Fn>(fn)(std::get<I>(std::forward<Tuple>(t))...);
953 }
954
955 template <std::size_t N, typename Tuple, typename Fn>
956 static void ApplyPrefix(Tuple &&t, Fn &&fn) {
957 ApplyPrefixImpl<N>(std::forward<Tuple>(t), std::forward<Fn>(fn),
958 std::make_index_sequence<N>{});
959 }
960
961 // Call `fn` with a runtime-selected prefix of `t`: exactly `call_arity`
962 // leading elements. `Is...` enumerates every compile-time count in
963 // `[0, sizeof...(Args)]`; the runtime check picks the matching one.
964 template <typename Tuple, std::size_t... Is, typename Fn>
965 static void CallWithArity(size_t call_arity, Tuple &&t,
966 std::index_sequence<Is...>, Fn &&fn) {
967 (void)std::initializer_list<int>{(
968 Is == call_arity
969 ? (ApplyPrefix<Is>(std::forward<Tuple>(t), std::forward<Fn>(fn)), 0)
970 : 0)...};
971 }
972
973 template <typename T, typename U>
974 void TransformBack(T &original_arg, U transformed_arg, Status &error) {
975 ReverseTransform(original_arg, transformed_arg, error);
976 }
977
978 // ScopedPythonObject is non-copyable — passing it through the generic
979 // TransformBack would trigger the deleted copy ctor. It manages its own
980 // cleanup via the destructor when the transformed-args tuple destructs, so
981 // there is nothing to reverse-transform back into the original arg.
982 template <typename T, typename SB>
983 void TransformBack(T &original_arg,
984 python::ScopedPythonObject<SB> &transformed_arg,
985 Status &error) {}
986
987 template <std::size_t... I, typename... Ts, typename... Us>
988 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
989 std::tuple<Us...> &transformed_args,
990 std::index_sequence<I...>) {
992 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),
993 error),
994 ...);
995 return error.Success();
996 }
997
998 template <typename... Ts, typename... Us>
999 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,
1000 std::tuple<Us...> &transformed_args) {
1001 if (sizeof...(Ts) != sizeof...(Us))
1002 return false;
1003
1004 return ReassignPtrsOrRefsArgs(original_args, transformed_args,
1005 std::make_index_sequence<sizeof...(Ts)>());
1006 }
1007
1008 template <typename T, typename... Args>
1009 void FormatArgs(std::string &fmt, T arg, Args... args) const {
1010 FormatArgs(fmt, arg);
1011 FormatArgs(fmt, args...);
1012 }
1013
1014 template <typename T> void FormatArgs(std::string &fmt, T arg) const {
1016 }
1017
1018 void FormatArgs(std::string &fmt) const {}
1019
1020 // The lifetime is managed by the ScriptInterpreter
1022};
1023
1024template <>
1025llvm::Expected<StructuredData::ArraySP>
1028
1029template <>
1030llvm::Expected<StructuredData::DictionarySP>
1033
1034template <>
1035llvm::Expected<Status>
1038
1039template <>
1040llvm::Expected<Event *>
1043
1044template <>
1045llvm::Expected<SymbolContext>
1048
1049template <>
1050llvm::Expected<lldb::StreamSP>
1053
1054template <>
1055llvm::Expected<lldb::ThreadSP>
1058
1059template <>
1060llvm::Expected<lldb::StackFrameSP>
1063
1064template <>
1065llvm::Expected<lldb::BreakpointSP>
1068
1069template <>
1070llvm::Expected<lldb::BreakpointLocationSP>
1073
1074template <>
1075llvm::Expected<lldb::ProcessAttachInfoSP>
1078
1079template <>
1080llvm::Expected<lldb::ProcessLaunchInfoSP>
1083
1084template <>
1085llvm::Expected<lldb::DataExtractorSP>
1088
1089template <>
1090llvm::Expected<lldb::ThreadPlanSP>
1093
1094template <>
1095llvm::Expected<std::optional<MemoryRegionInfo>>
1097 std::optional<MemoryRegionInfo>>(python::PythonObject &p);
1098
1099template <>
1100llvm::Expected<lldb::ExecutionContextRefSP>
1103
1104template <>
1105llvm::Expected<lldb::DescriptionLevel>
1108
1109template <>
1110llvm::Expected<lldb::StepType>
1113
1114template <>
1115llvm::Expected<lldb::StackFrameListSP>
1118
1119template <>
1120llvm::Expected<lldb::ValueObjectSP>
1123
1124template <>
1125llvm::Expected<lldb::TargetSP>
1128
1129template <>
1130llvm::Expected<lldb::ValueObjectListSP>
1133
1134template <>
1135llvm::Expected<std::optional<lldb::ValueType>>
1137 std::optional<lldb::ValueType>>(python::PythonObject &p);
1138
1139template <>
1140llvm::Expected<lldb::DebuggerSP>
1143
1144template <>
1145llvm::Expected<std::vector<std::string>>
1148
1149} // namespace lldb_private
1150
1151#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
#define LLDB_LOGF(log,...)
Definition Log.h:389
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
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
Guard PushScriptedExtensionCall()
Definition Policy.h:145
static PolicyStack & Get()
Definition Policy.cpp:21
std::optional< ScriptedMetadata > m_scripted_metadata
virtual llvm::SmallVector< AbstractMethodRequirement > GetAbstractMethodRequirements() const =0
virtual bool UserCanRunDirectly() const
Whether the user can invoke this extension directly, the way a scripted command can.
void RegisterInstance(const lldb::ScriptedInstanceRegistrySP &registry_sp, std::string class_name, FileSpec source_path, StructuredData::DictionarySP args_sp)
virtual llvm::SmallVector< llvm::StringLiteral > GetOptionalMethods() const
Methods a script may legitimately leave out, for which LLDB has a documented answer.
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)
Call method_name and fold both failure channels into one Status: a failure to call the method at all,...
python::PythonObject Transform(lldb::ProcessSP arg)
static llvm::Expected< FileSpec > GetModuleFile(llvm::StringRef module_name)
The file the already imported module_name was loaded from, read from its __file__.
static void ApplyPrefix(Tuple &&t, Fn &&fn)
void TransformBack(T &original_arg, U transformed_arg, Status &error)
python::PythonObject Transform(Event *arg)
python::PythonObject Transform(lldb::StepType arg)
auto TransformArgs(const std::tuple< Args... > &args)
python::PythonObject Transform(lldb::ExecutionContextRefSP arg)
python::PythonObject Transform(lldb::ThreadSP arg)
static T LogAndDefault(llvm::Expected< T > value_or_err, llvm::StringRef caller)
Log the failure in value_or_err and fall back to a default-constructed T.
python::PythonObject Transform(const StructuredDataImpl &arg)
llvm::Expected< T > ExtractValueFromPythonObject(python::PythonObject &p)
llvm::Error CheckAbstractMethods(const python::PythonObject &obj_class, llvm::StringRef qualified_class_name) const
Diagnose every abstract-method violation on obj_class at once.
~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)
llvm::Expected< T > CallStaticMethod(llvm::StringRef class_name, llvm::StringRef method_name, Args &&...args)
Call a static method on a Python class without creating an instance.
static llvm::Error LogAndError(llvm::StringRef caller_name, const char *format, Ts &&...ts)
Log message against caller_name and return it as an error.
llvm::Expected< T > Dispatch(llvm::StringRef method_name, Args &&...args)
Call method_name on the scripted object.
llvm::Expected< std::optional< T > > DispatchToOptional(llvm::StringRef method_name, Args &&...args)
Call an optional method_name, reporting "the script doesn't implementit" as std::nullopt rather than ...
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 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
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::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