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