//===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H #if LLDB_ENABLE_PYTHON #include #include #include #include #include #include "lldb/Host/Config.h" #include "lldb/Interpreter/ScriptedInterface.h" #include "lldb/Utility/DataBufferHeap.h" #include "PythonDataObjects.h" #include "SWIGPythonBridge.h" #include "ScriptInterpreterPythonImpl.h" namespace lldb_private { class ScriptInterpreterPythonImpl; class ScriptedPythonInterface : virtual public ScriptedInterface { public: ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter); ~ScriptedPythonInterface() override = default; protected: template T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { return p.CreateStructuredObject(); } template T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) { using namespace python; using Locker = ScriptInterpreterPythonImpl::Locker; std::string caller_signature = llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + llvm::Twine(method_name) + llvm::Twine(")")) .str(); if (!m_object_instance_sp) return ErrorWithMessage(caller_signature, "Python object ill-formed", error); Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); PythonObject implementor(PyRefType::Borrowed, (PyObject *)m_object_instance_sp->GetValue()); if (!implementor.IsAllocated()) return ErrorWithMessage(caller_signature, "Python implementor not allocated.", error); std::tuple original_args = std::forward_as_tuple(args...); auto transformed_args = TransformArgs(original_args); llvm::Expected expected_return_object = llvm::make_error("Not initialized.", llvm::inconvertibleErrorCode()); std::apply( [&implementor, &method_name, &expected_return_object](auto &&...args) { llvm::consumeError(expected_return_object.takeError()); expected_return_object = implementor.CallMethod(method_name.data(), args...); }, transformed_args); if (llvm::Error e = expected_return_object.takeError()) { error.SetErrorString(llvm::toString(std::move(e)).c_str()); return ErrorWithMessage(caller_signature, "Python method could not be called.", error); } PythonObject py_return = std::move(expected_return_object.get()); if (!py_return.IsAllocated()) return ErrorWithMessage(caller_signature, "Returned object is null.", error); // Now that we called the python method with the transformed arguments, // we need to interate again over both the original and transformed // parameter pack, and transform back the parameter that were passed in // the original parameter pack as references or pointers. if (sizeof...(Args) > 0) if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) return ErrorWithMessage( caller_signature, "Couldn't re-assign reference and pointer arguments.", error); return ExtractValueFromPythonObject(py_return, error); } template Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) { Status error; Dispatch(method_name, error, std::forward(args)...); return error; } template T Transform(T object) { // No Transformation for generic usage return {object}; } python::PythonObject Transform(Status arg) { return python::ToSWIGWrapper(arg); } template void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { // If U is not a PythonObject, don't touch it! return; } template void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, Status &error) { original_arg = ExtractValueFromPythonObject(transformed_arg, error); } template auto TransformTuple(const std::tuple &args, std::index_sequence) { return std::make_tuple(Transform(std::get(args))...); } // This will iterate over the Dispatch parameter pack and replace in-place // every `lldb_private` argument that has a SB counterpart. template auto TransformArgs(const std::tuple &args) { return TransformTuple(args, std::make_index_sequence()); } template void TransformBack(T &original_arg, U transformed_arg, Status &error) { ReverseTransform(original_arg, transformed_arg, error); } template bool ReassignPtrsOrRefsArgs(std::tuple &original_args, std::tuple &transformed_args, std::index_sequence) { Status error; (TransformBack(std::get(original_args), std::get(transformed_args), error), ...); return error.Success(); } template bool ReassignPtrsOrRefsArgs(std::tuple &original_args, std::tuple &transformed_args) { if (sizeof...(Ts) != sizeof...(Us)) return false; return ReassignPtrsOrRefsArgs(original_args, transformed_args, std::make_index_sequence()); } template void FormatArgs(std::string &fmt, T arg, Args... args) const { FormatArgs(fmt, arg); FormatArgs(fmt, args...); } template void FormatArgs(std::string &fmt, T arg) const { fmt += python::PythonFormat::format; } void FormatArgs(std::string &fmt) const {} // The lifetime is managed by the ScriptInterpreter ScriptInterpreterPythonImpl &m_interpreter; }; template <> StructuredData::ArraySP ScriptedPythonInterface::ExtractValueFromPythonObject( python::PythonObject &p, Status &error); template <> StructuredData::DictionarySP ScriptedPythonInterface::ExtractValueFromPythonObject< StructuredData::DictionarySP>(python::PythonObject &p, Status &error); template <> Status ScriptedPythonInterface::ExtractValueFromPythonObject( python::PythonObject &p, Status &error); template <> lldb::DataExtractorSP ScriptedPythonInterface::ExtractValueFromPythonObject( python::PythonObject &p, Status &error); template <> std::optional ScriptedPythonInterface::ExtractValueFromPythonObject< std::optional>(python::PythonObject &p, Status &error); } // namespace lldb_private #endif // LLDB_ENABLE_PYTHON #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H