1 //===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
12 #if LLDB_ENABLE_PYTHON
17 #include <type_traits>
20 #include "lldb/Host/Config.h"
21 #include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
22 #include "lldb/Utility/DataBufferHeap.h"
24 #include "../PythonDataObjects.h"
25 #include "../SWIGPythonBridge.h"
26 #include "../ScriptInterpreterPythonImpl.h"
28 namespace lldb_private
{
29 class ScriptInterpreterPythonImpl
;
30 class ScriptedPythonInterface
: virtual public ScriptedInterface
{
32 ScriptedPythonInterface(ScriptInterpreterPythonImpl
&interpreter
);
33 ~ScriptedPythonInterface() override
= default;
35 enum class AbstractMethodCheckerCases
{
39 eUnknownArgumentCount
,
40 eInvalidArgumentCount
,
44 struct AbstrackMethodCheckerPayload
{
46 struct InvalidArgumentCountPayload
{
47 InvalidArgumentCountPayload(size_t required
, size_t actual
)
48 : required_argument_count(required
), actual_argument_count(actual
) {}
50 size_t required_argument_count
;
51 size_t actual_argument_count
;
54 AbstractMethodCheckerCases checker_case
;
55 std::variant
<std::monostate
, InvalidArgumentCountPayload
> payload
;
58 llvm::Expected
<std::map
<llvm::StringLiteral
, AbstrackMethodCheckerPayload
>>
59 CheckAbstractMethodImplementation(
60 const python::PythonDictionary
&class_dict
) const {
62 using namespace python
;
64 std::map
<llvm::StringLiteral
, AbstrackMethodCheckerPayload
> checker
;
65 #define SET_CASE_AND_CONTINUE(method_name, case) \
67 checker[method_name] = {case, {}}; \
71 for (const AbstractMethodRequirement
&requirement
:
72 GetAbstractMethodRequirements()) {
73 llvm::StringLiteral method_name
= requirement
.name
;
74 if (!class_dict
.HasKey(method_name
))
75 SET_CASE_AND_CONTINUE(method_name
,
76 AbstractMethodCheckerCases::eNotImplemented
)
77 auto callable_or_err
= class_dict
.GetItem(method_name
);
78 if (!callable_or_err
) {
79 llvm::consumeError(callable_or_err
.takeError());
80 SET_CASE_AND_CONTINUE(method_name
,
81 AbstractMethodCheckerCases::eNotAllocated
)
84 PythonCallable callable
= callable_or_err
->AsType
<PythonCallable
>();
86 SET_CASE_AND_CONTINUE(method_name
,
87 AbstractMethodCheckerCases::eNotCallable
)
89 if (!requirement
.min_arg_count
)
90 SET_CASE_AND_CONTINUE(method_name
, AbstractMethodCheckerCases::eValid
)
92 auto arg_info_or_err
= callable
.GetArgInfo();
93 if (!arg_info_or_err
) {
94 llvm::consumeError(arg_info_or_err
.takeError());
95 SET_CASE_AND_CONTINUE(method_name
,
96 AbstractMethodCheckerCases::eUnknownArgumentCount
)
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
)
103 checker
[method_name
] = {
104 AbstractMethodCheckerCases::eInvalidArgumentCount
,
105 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload(
106 requirement
.min_arg_count
, arg_info
.max_positional_args
)};
110 #undef SET_CASE_AND_CONTINUE
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
;
122 Log
*log
= GetLog(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
)...)
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
) {
134 return create_error("Missing script class name.");
135 else if (!has_interpreter_dict
)
136 return create_error("Invalid script interpreter dictionary.");
138 return create_error("Missing scripting object.");
141 Locker
py_lock(&m_interpreter
, Locker::AcquireLock
| Locker::NoSTDIN
,
144 PythonObject result
= {};
147 result
= PythonObject(PyRefType::Borrowed
,
148 static_cast<PyObject
*>(script_obj
->GetValue()));
151 PythonModule::MainModule().ResolveName
<python::PythonDictionary
>(
152 m_interpreter
.GetDictionaryName());
153 if (!dict
.IsAllocated())
154 return create_error("Could not find interpreter dictionary: {0}",
155 m_interpreter
.GetDictionaryName());
158 PythonObject::ResolveNameWithDictionary
<python::PythonCallable
>(
160 if (!init
.IsAllocated())
161 return create_error("Could not find script class: {0}",
164 std::tuple
<Args
...> original_args
= std::forward_as_tuple(args
...);
165 auto transformed_args
= TransformArgs(original_args
);
167 std::string error_string
;
168 llvm::Expected
<PythonCallable::ArgInfo
> arg_info
= init
.GetArgInfo();
170 llvm::handleAllErrors(
171 arg_info
.takeError(),
172 [&](PythonException
&E
) { error_string
.append(E
.ReadBacktrace()); },
173 [&](const llvm::ErrorInfoBase
&E
) {
174 error_string
.append(E
.message());
176 return llvm::createStringError(llvm::inconvertibleErrorCode(),
180 llvm::Expected
<PythonObject
> expected_return_object
=
181 create_error("Resulting object is not initialized.");
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.
188 // This addresses the cases where the embedded interpreter session
189 // dictionary is passed to the extension initializer which is not used
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
);
199 [&init
, &expected_return_object
](auto &&...args
) {
200 llvm::consumeError(expected_return_object
.takeError());
201 expected_return_object
= init(args
...);
203 std::tuple_cat(transformed_args
, std::make_tuple(dict
)));
206 [&init
, &expected_return_object
](auto &&...args
) {
207 llvm::consumeError(expected_return_object
.takeError());
208 expected_return_object
= init(args
...);
213 if (!expected_return_object
)
214 return expected_return_object
.takeError();
215 result
= expected_return_object
.get();
218 if (!result
.IsValid())
219 return create_error("Resulting object is not a valid Python Object.");
220 if (!result
.HasAttribute("__class__"))
221 return create_error("Resulting object doesn't have '__class__' member.");
223 PythonObject obj_class
= result
.GetAttributeValue("__class__");
224 if (!obj_class
.IsValid())
225 return create_error("Resulting class object is not a valid.");
226 if (!obj_class
.HasAttribute("__name__"))
228 "Resulting object class doesn't have '__name__' member.");
229 PythonString obj_class_name
=
230 obj_class
.GetAttributeValue("__name__").AsType
<PythonString
>();
232 PythonObject object_class_mapping_proxy
=
233 obj_class
.GetAttributeValue("__dict__");
234 if (!obj_class
.HasAttribute("__dict__"))
236 "Resulting object class doesn't have '__dict__' member.");
238 PythonCallable dict_converter
= PythonModule::BuiltinsModule()
240 .AsType
<PythonCallable
>();
241 if (!dict_converter
.IsAllocated())
243 "Python 'builtins' module doesn't have 'dict' class.");
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.");
251 auto checker_or_err
= CheckAbstractMethodImplementation(object_class_dict
);
253 return checker_or_err
.takeError();
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 std::move(abstract_method_errors
),
261 std::move(create_error("Abstract method {0}.{1} not implemented.",
262 obj_class_name
.GetString(),
263 method_checker
.first
)));
265 case AbstractMethodCheckerCases::eNotAllocated
:
266 abstract_method_errors
= llvm::joinErrors(
267 std::move(abstract_method_errors
),
268 std::move(create_error("Abstract method {0}.{1} not allocated.",
269 obj_class_name
.GetString(),
270 method_checker
.first
)));
272 case AbstractMethodCheckerCases::eNotCallable
:
273 abstract_method_errors
= llvm::joinErrors(
274 std::move(abstract_method_errors
),
275 std::move(create_error("Abstract method {0}.{1} not callable.",
276 obj_class_name
.GetString(),
277 method_checker
.first
)));
279 case AbstractMethodCheckerCases::eUnknownArgumentCount
:
280 abstract_method_errors
= llvm::joinErrors(
281 std::move(abstract_method_errors
),
282 std::move(create_error(
283 "Abstract method {0}.{1} has unknown argument count.",
284 obj_class_name
.GetString(), method_checker
.first
)));
286 case AbstractMethodCheckerCases::eInvalidArgumentCount
: {
287 auto &payload_variant
= method_checker
.second
.payload
;
288 if (!std::holds_alternative
<
289 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload
>(
291 abstract_method_errors
= llvm::joinErrors(
292 std::move(abstract_method_errors
),
293 std::move(create_error(
294 "Abstract method {0}.{1} has unexpected argument count.",
295 obj_class_name
.GetString(), method_checker
.first
)));
297 auto payload
= std::get
<
298 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload
>(
300 abstract_method_errors
= llvm::joinErrors(
301 std::move(abstract_method_errors
),
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
)));
310 case AbstractMethodCheckerCases::eValid
:
311 LLDB_LOG(log
, "Abstract method {0}.{1} implemented & valid.",
312 obj_class_name
.GetString(), method_checker
.first
);
316 if (abstract_method_errors
) {
317 Status error
= Status::FromError(std::move(abstract_method_errors
));
318 LLDB_LOG(log
, "Abstract method error in {0}:\n{1}", class_name
,
320 return error
.ToError();
323 m_object_instance_sp
= StructuredData::GenericSP(
324 new StructuredPythonObject(std::move(result
)));
325 return m_object_instance_sp
;
329 template <typename T
= StructuredData::ObjectSP
>
330 T
ExtractValueFromPythonObject(python::PythonObject
&p
, Status
&error
) {
331 return p
.CreateStructuredObject();
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
;
339 std::string caller_signature
=
340 llvm::Twine(LLVM_PRETTY_FUNCTION
+ llvm::Twine(" (") +
341 llvm::Twine(method_name
) + llvm::Twine(")"))
343 if (!m_object_instance_sp
)
344 return ErrorWithMessage
<T
>(caller_signature
, "Python object ill-formed",
347 Locker
py_lock(&m_interpreter
, Locker::AcquireLock
| Locker::NoSTDIN
,
350 PythonObject
implementor(PyRefType::Borrowed
,
351 (PyObject
*)m_object_instance_sp
->GetValue());
353 if (!implementor
.IsAllocated())
354 return llvm::is_contained(GetAbstractMethods(), method_name
)
355 ? ErrorWithMessage
<T
>(caller_signature
,
356 "Python implementor not allocated.",
360 std::tuple
<Args
...> original_args
= std::forward_as_tuple(args
...);
361 auto transformed_args
= TransformArgs(original_args
);
363 llvm::Expected
<PythonObject
> expected_return_object
=
364 llvm::make_error
<llvm::StringError
>("Not initialized.",
365 llvm::inconvertibleErrorCode());
367 [&implementor
, &method_name
, &expected_return_object
](auto &&...args
) {
368 llvm::consumeError(expected_return_object
.takeError());
369 expected_return_object
=
370 implementor
.CallMethod(method_name
.data(), args
...);
374 if (llvm::Error e
= expected_return_object
.takeError()) {
375 error
= Status::FromError(std::move(e
));
376 return ErrorWithMessage
<T
>(caller_signature
,
377 "Python method could not be called.", error
);
380 PythonObject py_return
= std::move(expected_return_object
.get());
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
>(
390 "Couldn't re-assign reference and pointer arguments.", error
);
392 if (!py_return
.IsAllocated())
394 return ExtractValueFromPythonObject
<T
>(py_return
, error
);
397 template <typename
... Args
>
398 Status
GetStatusFromMethod(llvm::StringRef method_name
, Args
&&...args
) {
400 Dispatch
<Status
>(method_name
, error
, std::forward
<Args
>(args
)...);
405 template <typename T
> T
Transform(T object
) {
406 // No Transformation for generic usage
410 python::PythonObject
Transform(bool arg
) {
411 // Boolean arguments need to be turned into python objects.
412 return python::PythonBoolean(arg
);
415 python::PythonObject
Transform(const Status
&arg
) {
416 return python::SWIGBridge::ToSWIGWrapper(arg
.Clone());
419 python::PythonObject
Transform(Status
&&arg
) {
420 return python::SWIGBridge::ToSWIGWrapper(std::move(arg
));
423 python::PythonObject
Transform(const StructuredDataImpl
&arg
) {
424 return python::SWIGBridge::ToSWIGWrapper(arg
);
427 python::PythonObject
Transform(lldb::ExecutionContextRefSP arg
) {
428 return python::SWIGBridge::ToSWIGWrapper(arg
);
431 python::PythonObject
Transform(lldb::TargetSP arg
) {
432 return python::SWIGBridge::ToSWIGWrapper(arg
);
435 python::PythonObject
Transform(lldb::ProcessSP arg
) {
436 return python::SWIGBridge::ToSWIGWrapper(arg
);
439 python::PythonObject
Transform(lldb::ThreadPlanSP arg
) {
440 return python::SWIGBridge::ToSWIGWrapper(arg
);
443 python::PythonObject
Transform(lldb::ProcessAttachInfoSP arg
) {
444 return python::SWIGBridge::ToSWIGWrapper(arg
);
447 python::PythonObject
Transform(lldb::ProcessLaunchInfoSP arg
) {
448 return python::SWIGBridge::ToSWIGWrapper(arg
);
451 python::PythonObject
Transform(Event
*arg
) {
452 return python::SWIGBridge::ToSWIGWrapper(arg
);
455 python::PythonObject
Transform(lldb::StreamSP arg
) {
456 return python::SWIGBridge::ToSWIGWrapper(arg
.get());
459 python::PythonObject
Transform(lldb::DataExtractorSP arg
) {
460 return python::SWIGBridge::ToSWIGWrapper(arg
);
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!
469 template <typename T
>
470 void ReverseTransform(T
&original_arg
, python::PythonObject transformed_arg
,
472 original_arg
= ExtractValueFromPythonObject
<T
>(transformed_arg
, error
);
475 void ReverseTransform(bool &original_arg
,
476 python::PythonObject transformed_arg
, Status
&error
) {
477 python::PythonBoolean boolean_arg
= python::PythonBoolean(
478 python::PyRefType::Borrowed
, transformed_arg
.get());
479 if (boolean_arg
.IsValid())
480 original_arg
= boolean_arg
.GetValue();
482 error
= Status::FromErrorStringWithFormatv(
483 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION
);
486 template <std::size_t... I
, typename
... Args
>
487 auto TransformTuple(const std::tuple
<Args
...> &args
,
488 std::index_sequence
<I
...>) {
489 return std::make_tuple(Transform(std::get
<I
>(args
))...);
492 // This will iterate over the Dispatch parameter pack and replace in-place
493 // every `lldb_private` argument that has a SB counterpart.
494 template <typename
... Args
>
495 auto TransformArgs(const std::tuple
<Args
...> &args
) {
496 return TransformTuple(args
, std::make_index_sequence
<sizeof...(Args
)>());
499 template <typename T
, typename U
>
500 void TransformBack(T
&original_arg
, U transformed_arg
, Status
&error
) {
501 ReverseTransform(original_arg
, transformed_arg
, error
);
504 template <std::size_t... I
, typename
... Ts
, typename
... Us
>
505 bool ReassignPtrsOrRefsArgs(std::tuple
<Ts
...> &original_args
,
506 std::tuple
<Us
...> &transformed_args
,
507 std::index_sequence
<I
...>) {
509 (TransformBack(std::get
<I
>(original_args
), std::get
<I
>(transformed_args
),
512 return error
.Success();
515 template <typename
... Ts
, typename
... Us
>
516 bool ReassignPtrsOrRefsArgs(std::tuple
<Ts
...> &original_args
,
517 std::tuple
<Us
...> &transformed_args
) {
518 if (sizeof...(Ts
) != sizeof...(Us
))
521 return ReassignPtrsOrRefsArgs(original_args
, transformed_args
,
522 std::make_index_sequence
<sizeof...(Ts
)>());
525 template <typename T
, typename
... Args
>
526 void FormatArgs(std::string
&fmt
, T arg
, Args
... args
) const {
527 FormatArgs(fmt
, arg
);
528 FormatArgs(fmt
, args
...);
531 template <typename T
> void FormatArgs(std::string
&fmt
, T arg
) const {
532 fmt
+= python::PythonFormat
<T
>::format
;
535 void FormatArgs(std::string
&fmt
) const {}
537 // The lifetime is managed by the ScriptInterpreter
538 ScriptInterpreterPythonImpl
&m_interpreter
;
542 StructuredData::ArraySP
543 ScriptedPythonInterface::ExtractValueFromPythonObject
<StructuredData::ArraySP
>(
544 python::PythonObject
&p
, Status
&error
);
547 StructuredData::DictionarySP
548 ScriptedPythonInterface::ExtractValueFromPythonObject
<
549 StructuredData::DictionarySP
>(python::PythonObject
&p
, Status
&error
);
552 Status
ScriptedPythonInterface::ExtractValueFromPythonObject
<Status
>(
553 python::PythonObject
&p
, Status
&error
);
556 Event
*ScriptedPythonInterface::ExtractValueFromPythonObject
<Event
*>(
557 python::PythonObject
&p
, Status
&error
);
561 ScriptedPythonInterface::ExtractValueFromPythonObject
<lldb::StreamSP
>(
562 python::PythonObject
&p
, Status
&error
);
566 ScriptedPythonInterface::ExtractValueFromPythonObject
<lldb::BreakpointSP
>(
567 python::PythonObject
&p
, Status
&error
);
570 lldb::ProcessAttachInfoSP
ScriptedPythonInterface::ExtractValueFromPythonObject
<
571 lldb::ProcessAttachInfoSP
>(python::PythonObject
&p
, Status
&error
);
574 lldb::ProcessLaunchInfoSP
ScriptedPythonInterface::ExtractValueFromPythonObject
<
575 lldb::ProcessLaunchInfoSP
>(python::PythonObject
&p
, Status
&error
);
578 lldb::DataExtractorSP
579 ScriptedPythonInterface::ExtractValueFromPythonObject
<lldb::DataExtractorSP
>(
580 python::PythonObject
&p
, Status
&error
);
583 std::optional
<MemoryRegionInfo
>
584 ScriptedPythonInterface::ExtractValueFromPythonObject
<
585 std::optional
<MemoryRegionInfo
>>(python::PythonObject
&p
, Status
&error
);
588 lldb::ExecutionContextRefSP
589 ScriptedPythonInterface::ExtractValueFromPythonObject
<
590 lldb::ExecutionContextRefSP
>(python::PythonObject
&p
, Status
&error
);
592 } // namespace lldb_private
594 #endif // LLDB_ENABLE_PYTHON
595 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H